mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-03 08:28:31 +00:00
3.5.301
This commit is contained in:
parent
8db999a8a8
commit
8280b93c97
28 changed files with 2140 additions and 603 deletions
120
changelog.html
120
changelog.html
|
@ -1,3 +1,123 @@
|
||||||
|
<div class="list-header">3.5.302</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Bug causing chat pausing by scrolling up / moving the mouse / pressing a key to stop working.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.301</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Channel Bar on Bottom, Channel Title on Top, and Minimal Channel Bar settings to reflow the new channel design as the user wishes.</li>
|
||||||
|
<li>Fixed: Layout spacing tweaks.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.300</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>This... is... <em>breaking layout changes</em>!</li>
|
||||||
|
<li> </li>
|
||||||
|
<li>Fixed: Sidebar Width, Swap Sidebars, and Portrait Mode were all doing bad things with theater mode. They've been whipped into shape.</li>
|
||||||
|
<li>Fixed: That one lighter-than-it-should-be bar separating channel title and info if you had dark theme.</li>
|
||||||
|
<li>Fixed: Host mode's ugly dark borders were not just ugly, but too purpley. Fixed.</li>
|
||||||
|
<li> </li>
|
||||||
|
<li>Still Broken: Some minor spacing issues, Auto-Theater Mode, disabling Hosting, extra following buttons, SRL races. Not looking like this.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.299</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Command Aliases! Define custom commands for chat that are shortcuts for other commands! Madness! (pls no recursion)</li>
|
||||||
|
<li>API Added: <code>hidden</code> property for emote sets to keep them out of the emote menu.</li>
|
||||||
|
<li>API Fixed: API instances were storing incorrect emote set IDs (the global FFZ IDs instead of the per-API IDs specifically).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.298</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: <code>/rules</code> chat command to show the chat rules for a room.</li>
|
||||||
|
<li>Added: <code>/open_link</code> chat command for use with moderation actions.</li>
|
||||||
|
<li>Added: <code>/fetch_link</code> chat command for user with moderation actions.</li>
|
||||||
|
<li>Changed: Completely redo the UI for setting in-line mod icons and additional mod card buttons.</li>
|
||||||
|
<li>Changed: Add extra variables to custom in-line mod icons and additional mod card buttons.</li>
|
||||||
|
<li>Changed: Allow the use of emoji for in-line mod icons.</li>
|
||||||
|
<li>Fixed: Ensure moderation logs are compared in a case-sensitive manner.</li>
|
||||||
|
<li>Fixed: Don't add a message to a chat room if that message ID already exists.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.297</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Stuff.</li>
|
||||||
|
<li>Fixed: Append historical messages to chatter history in the correct order.</li>
|
||||||
|
<li>Fixed: Bug with the rendering of chat room states, specifically regarding Delay.</li>
|
||||||
|
<li>Fixed: Treat the front page of Twitch as a standard part of the Ember app to avoid only partially loading FFZ elsewhere.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.296</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Bug with a straight timeout not working with the next context menu.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.295</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Accidental debugging code made the new context menu always use the last option. I'm a dumb.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.294</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Context menu for in-line moderation icons to let moderators easily apply a reason to their ban or timeout.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.293</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: CBenni memes.</li>
|
||||||
|
<li>Changed: The Delay badge will be faded to indicate that it's disabled if you are a moderator who doesn't experience chat delay in a room.</li>
|
||||||
|
<li>Fixed: The Slow Mode status indicator would not display in some situations.</li>
|
||||||
|
<li>Fixed: Badges not rendering correctly in the chat menu's preview.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.292</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Timestamps rendering as <code>undefined</code> in chat under certain situations.</li>
|
||||||
|
<li>Fixed: Message history showing in reverse order.</li>
|
||||||
|
<li>Removed: FFZ's experimental chat history that had very limited server-side support.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.291</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Enhanced channel information, including clickable links in titles, stream uptime, stream latency, the Host button, and chatter count should all be working now. The channel model was reworked and I had to update my code to match.</li>
|
||||||
|
<li>Fixed: FFZ settings weren't appearing in the chat settings menu.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.290</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Unable to tab complete usernames. It works now. (For some reason, the suggestions property is now a function I have to call? Okay Twitch.)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.289</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Username colors were not being calculated properly. (Twitch moved the settings controller and so FFZ didn't realize chat was dark.)</li>
|
||||||
|
<li>Fixed: Mod icons could potentially be a bit screwy for the same reason.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.288</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Proper fix for chat messages not appearing. Rewrote the message queue.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.287</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Temporary fix for chat messages not appearing as they should.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.286</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Fixed: Disabled two debugging logging statements that were erroneously left intact.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">3.5.285</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Show FFZ badges in the Name Display section of the Twitch chat settings menu.</li>
|
||||||
|
<li>Added: Basic regular expression support for highlighting and banning words.</li>
|
||||||
|
<li>Fixed: Update the pubsub patch for chat for the new topics.</li>
|
||||||
|
<li>Changed: Logic tweaks to make the Show Moderation Actions setting in the Twitch chat settings menu more effectual with FFZ present.</li>
|
||||||
|
<li>Changed: Dark CSS tweaks.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="list-header">3.5.284</div>
|
<div class="list-header">3.5.284</div>
|
||||||
<ul class="chat-menu-content menu-side-padding">
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
<li>Changed: Inject FFZ emotes into the BetterTTV tab completion list.</li>
|
<li>Changed: Inject FFZ emotes into the BetterTTV tab completion list.</li>
|
||||||
|
|
17
credits.html
Normal file
17
credits.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="list-header">Awesome People</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li><a href="//twitch.tv/fugi" target="_blank">Fugi</a> - Wrote the Chat Delay feature.</li>
|
||||||
|
<li><a href="//twitch.tv/daxterspeed" target="_blank">DaxterSpeed</a> - Wrote a method for adjusting username colors.</li>
|
||||||
|
<li><a href="//twitch.tv/riking27" target="_blank">Riking</a> - Wrote a large part of the socket server system.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="list-header">Awesome Resources</div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li><a href="https://letsencrypt.org/" target="_blank">Let's Encrypt</a> - Free and Automated SSL for Everyone</li>
|
||||||
|
<li><a href="https://github.com/eligrey/FileSaver.js" target="_blank">FileSaver.js</a> - saveAs() implementation</li>
|
||||||
|
<li><a href="http://objectpath.org/" target="_blank">ObjectPath</a> - Lightweight query language for JSON</li>
|
||||||
|
<li><a href="https://github.com/twitter/twemoji" target="_blank">Twitter Emoji for Everyone</a> - Twitter's Emoji</li>
|
||||||
|
<li><a href="https://github.com/googlei18n/noto-emoji" target="_blank">Noto Emoji</a> - Google's Noto Emoji</li>
|
||||||
|
<li><a href="http://emojione.com/" target="_blank">EmojiOne</a> - An open source emoji project</li>
|
||||||
|
</ul>
|
67
dark.css
67
dark.css
|
@ -200,9 +200,15 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
|
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .balloon--tooltip:after,
|
||||||
|
.ffz-dark .balloon--tooltip {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
.ffz-dark #flyout .content,
|
.ffz-dark #flyout .content,
|
||||||
.ffz-dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box { border: none }
|
.ffz-dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box { border: none }
|
||||||
|
|
||||||
|
.ffz-dark .balloon--right.balloon--up:after,
|
||||||
.ffz-dark .balloon--up:after { box-shadow: 1px 1px 0 rgba(255,255,255,0.2) }
|
.ffz-dark .balloon--up:after { box-shadow: 1px 1px 0 rgba(255,255,255,0.2) }
|
||||||
.ffz-dark .balloon--right.balloon--down:after,
|
.ffz-dark .balloon--right.balloon--down:after,
|
||||||
.ffz-dark .balloon--down:after { box-shadow: -1px -1px 0 rgba(255,255,255,0.2) }
|
.ffz-dark .balloon--down:after { box-shadow: -1px -1px 0 rgba(255,255,255,0.2) }
|
||||||
|
@ -317,6 +323,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
|
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
|
||||||
.ffz-dark .dropmenu_action:not(:hover),
|
.ffz-dark .dropmenu_action:not(:hover),
|
||||||
.ffz-dark .dropmenu_action:not(:hover) span,
|
.ffz-dark .dropmenu_action:not(:hover) span,
|
||||||
|
.ffz-dark .chat-menu-content button.font-color-purple,
|
||||||
.ffz-dark a {
|
.ffz-dark a {
|
||||||
/*.ffz-dark a:not(.profile-card__content):not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) {*/
|
/*.ffz-dark a:not(.profile-card__content):not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) {*/
|
||||||
color: #a68ed2;
|
color: #a68ed2;
|
||||||
|
@ -365,6 +372,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
color: #a68ed2;
|
color: #a68ed2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .button--icon.button--hollow figure svg,
|
||||||
.ffz-dark .button.button--icon-only svg {
|
.ffz-dark .button.button--icon-only svg {
|
||||||
fill: #a68ed2;
|
fill: #a68ed2;
|
||||||
}
|
}
|
||||||
|
@ -534,6 +542,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
background-color: rgb(16,16,16);
|
background-color: rgb(16,16,16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .card__title,
|
||||||
.ffz-dark .card .card__title a,
|
.ffz-dark .card .card__title a,
|
||||||
.ffz-dark .card .card__info a,
|
.ffz-dark .card .card__info a,
|
||||||
.ffz-dark .items-grid .meta .title,
|
.ffz-dark .items-grid .meta .title,
|
||||||
|
@ -552,6 +561,17 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
color: #a68ed2;
|
color: #a68ed2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .tabs { box-shadow: 0 -1px 0 rgba(255,255,255,0.2) inset; }
|
||||||
|
|
||||||
|
.ffz-dark .tabs>.tab:hover,
|
||||||
|
.ffz-dark .tabs>li:hover,
|
||||||
|
.ffz-dark .tabs__item:hover,
|
||||||
|
.ffz-dark .tabs__item--active,
|
||||||
|
.ffz-dark .tabs__item.active,
|
||||||
|
.ffz-dark .tabs__item.selected,
|
||||||
|
.ffz-dark .tabs>li.selected,
|
||||||
|
.ffz-dark .tabs>li>a.active { box-shadow: 0 -1px 0 #a68cd4 inset }
|
||||||
|
|
||||||
.ffz-dark .mininav,
|
.ffz-dark .mininav,
|
||||||
.ffz-dark ul.tabs:before,
|
.ffz-dark ul.tabs:before,
|
||||||
.ffz-dark .direcotry_header .nav:before,
|
.ffz-dark .direcotry_header .nav:before,
|
||||||
|
@ -856,7 +876,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-dark .js-show-stream-key { background-color: #804400; }
|
.ffz-dark .js-show-stream-key { background-color: #804400; }
|
||||||
.ffz-dark .js-reset-stream-key { r-color: rgb(128,0,0); }
|
.ffz-dark .js-reset-stream-key { border-color: rgb(128,0,0); }
|
||||||
|
|
||||||
.ffz-dark .js-show-stream-key:hover,
|
.ffz-dark .js-show-stream-key:hover,
|
||||||
.ffz-dark .js-show-stream-key:focus,
|
.ffz-dark .js-show-stream-key:focus,
|
||||||
|
@ -1417,3 +1437,48 @@ body.ffz-dark:not([data-page="teams#show"]),
|
||||||
.ffz-dark .resultView__titlesep {
|
.ffz-dark .resultView__titlesep {
|
||||||
background-color: #090909;
|
background-color: #090909;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Channel Redesign */
|
||||||
|
|
||||||
|
.ffz-dark .cn-bar {
|
||||||
|
background-color: #101010;
|
||||||
|
box-shadow: inset 0 -1px 0 #161616, -1px 1px rgba(255,255,255,0.065);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-metabar__more {
|
||||||
|
border-color: rgba(255,255,255,0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz-dark .button--icon.cn-bar__button--subscribed {
|
||||||
|
background-color: #161616;
|
||||||
|
box-shadow: 0 0 0 1px rgba(255,255,255,0.065) inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz-dark .cn-bar__avatar-wrap {
|
||||||
|
background-color: #101010;
|
||||||
|
border-color: #101010;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-bar__displayname {
|
||||||
|
color: #c3c3c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-tabs__item--withseparator:after {
|
||||||
|
border-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-tabs__item > a {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-tabs__item > a.active,
|
||||||
|
.ffz-dark .cn-tabs__item > a:hover {
|
||||||
|
color: #a68ed2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .cn-tabs__item > a:hover:after {
|
||||||
|
border-color: #a68ed2;
|
||||||
|
}
|
21
src/ObjectPath.js
Normal file
21
src/ObjectPath.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
String.prototype.tokens=function(h){h=h?h:!1;var a,g,b=0,k=this.length,f,c,e=[],l="and;or;not;in;not in;is;is not;".split(";"),d=function(a,c,d){if(!d)return{type:a,value:c,position:[g,b]}};if(this){for(a=this.charAt(b);a;)if(g=b," ">=a)b+=1,a=this.charAt(b);else if("a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"==a){c=a;for(b+=1;;)if(a=this.charAt(b),"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"0"<=a&&"9">=a||"_"===a)c+=a,b+=1;else break;0<=l.indexOf(c)?(f=e[e.length-1],"not"===c&&f&&"is"===f.value?(c=d("op","is not"),
|
||||||
|
c.position[0]=f.position[0],e[e.length-1]=c):"in"===c&&f&&"not"===e[e.length-1].value?(c=d("op","not in"),c.position[0]=f.position[0],e[e.length-1]=c):e.push(d("op",c))):e.push(d("name",c))}else if("0"<=a&&"9">=a){c=a;for(b+=1;;){a=this.charAt(b);if("0">a||"9"<a)break;b+=1;c+=a}if("."===a)for(b+=1,c+=a;;){a=this.charAt(b);if("0">a||"9"<a)break;b+=1;c+=a}if("e"===a||"E"===a){b+=1;c+=a;a=this.charAt(b);if("-"===a||"+"===a)b+=1,c+=a,a=this.charAt(b);("0">a||"9"<a)&&d("number",c,"Bad exponent");do b+=
|
||||||
|
1,c+=a,a=this.charAt(b);while("0"<=a&&"9">=a)}"a"<=a&&"z">=a&&(c+=a,b+=1,d("number",c,"Bad number"));f=+c;isFinite(f)?e.push(d("number",f)):d("number",c,"Bad number")}else if("'"===a||'"'===a){c="";f=a;for(b+=1;;){a=this.charAt(b);" ">a&&d("str",c,"\n"===a||"\r"===a||""===a?"Unterminated string.":"Control character in string.",d("",c));if(a===f)break;if("\\"===a)switch(b+=1,b>=k&&d("str",c,"Unterminated string"),a=this.charAt(b),a){case "b":a="\b";break;case "f":a="\f";break;case "n":a="\n";break;
|
||||||
|
case "r":a="\r";break;case "t":a="\t";break;case "u":b>=k&&d("str",c,"Unterminated string"),a=parseInt(this.substr(b+1,4),16),(!isFinite(a)||0>a)&&d("str",c,"Unterminated string"),a=String.fromCharCode(a),b+=4}c+=a;b+=1}b+=1;e.push(d("str",c));a=this.charAt(b)}else 0<="<>.".indexOf(a)?(c=a,b+=1,a=this.charAt(b),"<"===c&&"="===a?c="<=":">"===c&&"="===a?c=">=":"."===c&&"."===a&&(c=".."),1<c.length&&(b+=1,a=this.charAt(b)),e.push(d("op",c))):(b+=1,"$"===a?e.push(d("(root)",a)):"@"===a?e.push(d("(current)",
|
||||||
|
a)):"!"===a?e.push(d("(context)",a)):0<="+-*/%<>:[,]{}:()".indexOf(a)?e.push(d("op",a)):d("op",a,a+" is not an ObjectPath operator!"),a=this.charAt(b));h&&clog("}tokens with",e);return e}};
|
||||||
|
var makeTree=function(){var h={},e,n,l,u=["true","t"],v=["false","f"],w=["none","null","n","nil"],m=function(){return this},f=function(a){var b,c,d;a&&e.id!==a&&e.error("Expected '"+a+"', got '"+e.id+"'.");if(l>=n.length)e=h["(end)"];else return c=n[l],l+=1,d=c.value,a=c.type,"name"===a?(0<=v.indexOf(d.toLowerCase())&&(d="false"),0<=u.indexOf(d.toLowerCase())&&(d="true"),0<=w.indexOf(d.toLowerCase())&&(d="null"),(b=h[d])||(b=h["(name)"])):"(root)"===a?b=h["(root)"]:"(current)"===a?b=h["(current)"]:
|
||||||
|
"(context)"===a?b=h["(context)"]:"op"===a?(b=h[d])||c.error("Unknown operator."):"str"===a||"number"===a?(b=h["(literal)"],a="literal"):c.error("Unexpected token."),e=Object.create(b),e.position=c.position,e.value=d,e.arity=a,e.error=function(a){},e},k=function(a){var b,c=e;a="undefined"===typeof a?0:a;f();for(b=c.nud();a<e.lbp;)c=e,f(),b=c.led(b);return b},x={nud:function(){this.error("Undefined nud().")},led:function(a){this.error("Missing operator.")}},d=function(a,b){var c=h[a];b=b||0;c?b>=c.lbp&&
|
||||||
|
(c.lbp=b):(c=Object.create(x),c.id=c.value=a,c.lbp=b,h[a]=c);return c},p=function(a,b){var c=d(a);c.nud=function(){this.value=h[this.id].value;this.arity="literal";this.id="(literal)";return this};c.value=b;return c},g=function(a,b,c){a=d(a,b);a.led=c||function(a){this.first=a;this.second=k(b);this.arity="binary";return this};return a},r=function(a,b,c){a=d(a,b);a.led=c||function(a){this.first=a;this.second=k(b-1);this.arity="binary";return this};return a},q=function(a,b,c){a=d(a);a.nud=c||function(){this.first=
|
||||||
|
k(b);this.arity="unary";return this};return a},t=function(a){this.first=a;0>["(name)","*"].indexOf(e.id)&&SyntaxError("Expected an attribute name.");"*"===e.id&&(e.arity="wildcard");this.second=e;f();return this};d("(end)");d("(name)").nud=m;d("(literal)").nud=m;d("(root)").nud=m;d("(current)").nud=m;d("(context)").nud=m;d(":");d(",");p("true",!0);p("false",!1);p("null",null);r("or",30);r("and",40);q("not",50);g(":",120);g("in",60);g("not in",60);g("is",60);g("is not",60);g("<",60);g("<=",60);g(">",
|
||||||
|
60);g(">=",60);g("+",110);g("-",110);g("*",120);g("/",120);g("%",120);q("-",130);q("+",130);d(".",150).led=t;d("..",150).led=t;d("]");g("[",150,function(a){this.first=a;this.second=k(0);this.arity="binary";f("]");return this});d("[").led=function(a){this.first=a;this.second=k();f("]");return this};d("[").nud=function(){var a=[];if("]"!==e.id)for(;;){a.push(k());if(","!==e.id)break;f(",")}f("]");this.first=a;this.arity="unary";return this};d(")");d("(",150).led=function(a){var b=[];this.arity="binary";
|
||||||
|
this.id="fn";this.first=a.value;if(")"!==e.id)for(;;){b.push(k());if(","!==e.id)break;f(",")}f(")");this.second=b;return this};d("(",150).nud=function(){var a=k();f(")");return a};d("}");d("{").nud=function(){var a=[],b,c;if("}"!==e.id)for(;;){b=k();f(":");c=k();c.key=b;a.push(c);if(","!==e.id)break;f(",")}f("}");this.first=a;this.arity="unary";return this};return function(a,b){var c=this.D=b&&b.debug||!1;n="string"===typeof a?a.tokens(c):a;l=0;f();c=k();f("(end)");return c}},parse=makeTree();
|
||||||
|
module.exports = ObjectPath=function(a,h){this.exprCache=[];this._init_(a,h);return this};
|
||||||
|
ObjectPath.prototype={D:!1,current:null,data:null,SELECTOR_OPS:"is;>;<;is not;>=;<=;in;not in;:;and;or".split(";"),simpleTypes:["string","number"],_init_:function(a,h){this.setData(a);h&&this.setDebug(h.debug||this.D)},setCurrent:function(a){this.current=a},resetCurrent:function(){this.current=null},setContext:function(a){this.context=a},compile:function(a){return this.exprCache.hasOwnProperty(a)?this.exprCache[a]:this.exprCache[a]=parse.call(this,a,{debug:this.D})},setData:function(a){return 0>["object",
|
||||||
|
"array"].indexOf(typeof a)?(this.log(a+" is not object nor array! Data not changed."),a):this.data=a},setDebug:function(a){this.D=a},flatten:function(a){var h=[],l=function(a){if(Array.isArray(a))for(var g=0;g<a.length;g++)l(a[g]);else if("object"===typeof a)for(g in h.push(a),a)l(a[g])};l(a);return h},execute:function(a){var h=this,l=this.flatten,m=this.simpleTypes,g,f=function(b){if(Array.isArray(b)){for(var c=[],e=0;e<b.length;e++)c.push(f(b[e]));return c}var a=b.id;if(0<="+;-;*;%;/;>;>=;<;<=;in;not in;is;is not".split(";").indexOf(b.id))var c=
|
||||||
|
"object"===typeof b.first&&b.first.id?f(b.first):null,d="object"===typeof b.second&&b.second.id?f(b.second):null,g=typeof c,k=typeof d;switch(a){case "(literal)":return b.value;case "+":if("number"===g&&"number"!==k)d=parseInt(d);else if(Array.isArray(c)){if(Array.isArray(d))return c.concat(d);c.push(d)}return c+d;case "-":return d?c-d:-c;case "*":return"wildcard"!=b.arity?c*d:null;case "%":return c%d;case "/":return c/d;case ">":return c>d;case "<":return c<d;case ">=":return c>=d;case "<=":return c<=
|
||||||
|
d;case "not":return!f(b.first);case "or":return f(b.first)||f(b.second);case "and":return f(b.first)&&f(b.second);case "in":case "not in":return e=null,"string"===k?e=0<=d.search(c.toString()):Array.isArray(d)?e=0<=d.indexOf(c):"object"===k&&(e=d.hasOwnProperty(c)),"in"===a?e:!e;case "is":case "is not":e=null;if(0<=m.indexOf(g))e=c==d;else if("array"===g||"object"===g)try{throw e=JSON.stringify(c)==JSON.stringify(d),{name:"comparison Error",message:"JSONStringifyNotAvailable. Your web browser doesn't support JSON.stringify(). Vote for support at"};
|
||||||
|
}catch(n){}return"is"===a?e:!e;case "(root)":return h.data;case "(current)":return h.current;case "(context)":return h.context;case ":":return b;case "(name)":return b.value;case "[":if("unary"===b.arity){a=[];for(e in b.first)a.push(f(b.first[e]));return a}if("op"===b.arity){c=f(b.first);if(!c)return c;if("string"===typeof c||Array.isArray(c)){d=f(b.second);k=typeof d;if(d&&":"===d.id)return c.slice(f(d.first),f(d.second));if("number"===k)return-1===d?c.slice(-1)[0]:0>d?c.slice(d,d+1)[0]:c[d];if("string"===
|
||||||
|
k){a=[];for(e=0;e<c.length;e++)c[e][d]&&a.push(c[e][d]);return a}if("object"===typeof b.second&&0<=h.SELECTOR_OPS.indexOf(b.second.id)){b=b.second;a=[];b=Object.create(b);for(e in c)d=c[e],h.current=d,b.first=d,f(b)&&a.push(d);return a}programmingError("left is array and right is not number")}else if("object"===g&&("(name)"===b.second.id||"string"===k))return c[d];return 1}return null;case "(":switch(b.first.value){case "str":return e=f(b.second),"object"===typeof e?JSON.stringify(e):e.toString}return null;
|
||||||
|
case "{":case "":throw{error:"NotImplementedYet",message:a+" is not implemented yet!",data:b};case "..":c=l(f(b.first));case ".":c=c||f(b.first);if("*"===b.second.id)return c;if(c){if(Array.isArray(c)){a=[];d=f(b.second);for(e=0;e<c.length;e++)c[e][d]&&a.push(c[e][d]);return a}return c[b.second.value]}return null;case "fn":switch(b.first){case "float":case "int":return parseFloat(f(b.second));case "str":return f(b.second).toString();case "array":return e=f(b.second),Array.isArray(e[0])?e[0]:"string"===
|
||||||
|
typeof e[0]?e[0].split(""):[];case "replace":return a=f(b.second),a[0]?a[0].replace(new RegExp(a[1],"g"),a[2]):"";case "join":a=f(b.second);try{return a[0].join(a[1])}catch(p){return null}case "split":return a=f(b.second),a[0]?a[0].split(a[1]):"";case "max":return Math.max.apply(null,f(b.second)[0]);case "min":return Math.min.apply(null,f(b.second)[0])}throw{error:"WrongFunction",message:"Function "+a+" is not proper ObjectPath function.",data:b};}throw{error:"WrongOperator",message:"Operator "+a+
|
||||||
|
" is not proper ObjectPath operator.",data:b};};if(!a)return a;"string"===typeof a&&(g=this.compile(a));return f(g)}};
|
|
@ -158,15 +158,15 @@ FFZ.prototype.setup_colors = function() {
|
||||||
|
|
||||||
// Events for rebuilding colors.
|
// Events for rebuilding colors.
|
||||||
var Layout = utils.ember_lookup('service:layout'),
|
var Layout = utils.ember_lookup('service:layout'),
|
||||||
Settings = utils.ember_lookup('controller:settings');
|
Settings = utils.ember_settings();
|
||||||
|
|
||||||
if ( Layout )
|
if ( Layout )
|
||||||
Layout.addObserver("isTheatreMode", this._update_colors.bind(this, true));
|
Layout.addObserver("isTheatreMode", this._update_colors.bind(this, true));
|
||||||
|
|
||||||
if ( Settings )
|
if ( Settings )
|
||||||
Settings.addObserver("settings.darkMode", this._update_colors.bind(this, true))
|
Settings.addObserver("darkMode", this._update_colors.bind(this, true))
|
||||||
|
|
||||||
this._color_old_darkness = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
|
this._color_old_darkness = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('darkMode'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -685,9 +685,8 @@ FFZ.prototype._rebuild_colors = function() {
|
||||||
FFZ.prototype._update_colors = function(darkness_only) {
|
FFZ.prototype._update_colors = function(darkness_only) {
|
||||||
// Update the lines. ALL of them.
|
// Update the lines. ALL of them.
|
||||||
var Layout = utils.ember_lookup('service:layout'),
|
var Layout = utils.ember_lookup('service:layout'),
|
||||||
Settings = utils.ember_lookup('controller:settings'),
|
|
||||||
|
|
||||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')),
|
is_dark = (Layout && Layout.get('isTheatreMode')) || this.settings.get_twitch("darkMode"),
|
||||||
cr_dark = this.settings.dark_twitch || (Layout && Layout.get('isTheatreMode'));
|
cr_dark = this.settings.dark_twitch || (Layout && Layout.get('isTheatreMode'));
|
||||||
|
|
||||||
if ( darkness_only && this._color_old_darkness === is_dark )
|
if ( darkness_only && this._color_old_darkness === is_dark )
|
||||||
|
|
205
src/commands.js
205
src/commands.js
|
@ -1,5 +1,132 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
utils = require('./utils');
|
constants = require('./constants'),
|
||||||
|
utils = require('./utils'),
|
||||||
|
|
||||||
|
KNOWN_COMMANDS = ['ffz', 'unban', 'ban', 'timeout', 'r9kbeta', 'r9kbetaoff', 'slow', 'slowoff', 'subscribers', 'subscribersoff', 'mod', 'unmod', 'me', 'emotesonly', 'emotesonlyoff', 'host', 'unhost', 'commercial'],
|
||||||
|
|
||||||
|
STATUS_CODES = {
|
||||||
|
400: "Bad Request",
|
||||||
|
401: "Unauthorized",
|
||||||
|
403: "Forbidden",
|
||||||
|
404: "Not Found",
|
||||||
|
500: "Internal Server Error"
|
||||||
|
},
|
||||||
|
|
||||||
|
format_result = function(response) {
|
||||||
|
if ( typeof response === "string" )
|
||||||
|
return response;
|
||||||
|
|
||||||
|
else if ( Array.isArray(response) )
|
||||||
|
return _.map(response, format_result).join(", ");
|
||||||
|
|
||||||
|
return JSON.stringify(response);
|
||||||
|
},
|
||||||
|
|
||||||
|
ObjectPath = require('./ObjectPath');
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.ObjectPath = ObjectPath;
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// Settings
|
||||||
|
// -----------------
|
||||||
|
|
||||||
|
FFZ.settings_info.command_aliases = {
|
||||||
|
type: "button",
|
||||||
|
value: [],
|
||||||
|
|
||||||
|
category: "Chat Moderation",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Command Aliases",
|
||||||
|
help: "Define custom commands for chat that are shortcuts for other commands or messages to send in chat.",
|
||||||
|
|
||||||
|
on_update: function() {
|
||||||
|
this.cache_command_aliases();
|
||||||
|
},
|
||||||
|
|
||||||
|
method: function() {
|
||||||
|
var f = this,
|
||||||
|
old_val = [],
|
||||||
|
input = utils.createElement('textarea');
|
||||||
|
|
||||||
|
input.style.marginBottom = '20px';
|
||||||
|
|
||||||
|
for(var i=0; i < this.settings.command_aliases.length; i++) {
|
||||||
|
var pair = this.settings.command_aliases[i],
|
||||||
|
name = pair[0],
|
||||||
|
command = pair[1];
|
||||||
|
|
||||||
|
old_val.push(name + '=' + command);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.prompt(
|
||||||
|
"Command Aliases",
|
||||||
|
"Please enter a list of custom commands that you would like to use in Twitch chat. " +
|
||||||
|
"One item per line. To send multiple commands, separate them with <code><LINE></code>. " +
|
||||||
|
"Variables, such as arguments you provide running the custom command, can be inserted into the output.<hr>" +
|
||||||
|
|
||||||
|
"All custom commands require names. Names go at the start of each line, and are separated from " +
|
||||||
|
" the actual command by an equals sign. Do not include the leading slash or dot. Those are automatically included.<br>" +
|
||||||
|
"<strong>Example:</strong> <code>boop=/timeout {0} 15 Boop!</code><hr>" +
|
||||||
|
|
||||||
|
"<code>{0}</code>, <code>{1}</code>, <code>{2}</code>, etc. will be replaced with any arguments you've supplied. " +
|
||||||
|
"Follow an argument index with a <code>$</code> to also include all remaining arguments.<br>" +
|
||||||
|
"<strong>Example:</strong> <code>boop=/timeout {0} 15 {1$}</code><hr>" +
|
||||||
|
|
||||||
|
"<strong>Allowed Variables</strong><br><table><tbody>" +
|
||||||
|
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
|
||||||
|
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
|
||||||
|
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
|
||||||
|
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
|
||||||
|
"</tbody></table>",
|
||||||
|
old_val.join("\n"),
|
||||||
|
function(new_val) {
|
||||||
|
if ( new_val === null || new_val === undefined )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var vals = new_val.trim().split(/\s*\n\s*/g),
|
||||||
|
output = [];
|
||||||
|
|
||||||
|
for(var i=0; i < vals.length; i++) {
|
||||||
|
var cmd = vals[i],
|
||||||
|
name,
|
||||||
|
name_match = /^([^=]+)=/.exec(cmd);
|
||||||
|
|
||||||
|
if ( ! cmd || ! cmd.length )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( name_match ) {
|
||||||
|
name = name_match[1].toLowerCase();
|
||||||
|
cmd = cmd.substr(name_match[0].length);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push([name, cmd]);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.settings.set("command_aliases", output);
|
||||||
|
}, 600, input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype._command_aliases = {};
|
||||||
|
|
||||||
|
FFZ.prototype.cache_command_aliases = function() {
|
||||||
|
var aliases = this._command_aliases = {};
|
||||||
|
for(var i=0; i < this.settings.command_aliases.length; i++) {
|
||||||
|
var pair = this.settings.command_aliases[i],
|
||||||
|
name = pair[0],
|
||||||
|
command = pair[1];
|
||||||
|
|
||||||
|
// Skip taken/invalid names.
|
||||||
|
if ( ! name || ! name.length || aliases[name] || KNOWN_COMMANDS.indexOf(name) !== -1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
aliases[name] = command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------
|
// -----------------
|
||||||
|
@ -117,6 +244,82 @@ FFZ.chat_commands.card = function(room, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.chat_commands.rules = function(room, args) {
|
||||||
|
var f = this,
|
||||||
|
r = room.room;
|
||||||
|
|
||||||
|
r.waitForRoomProperties().then(function() {
|
||||||
|
var rules = r.get("roomProperties.chat_rules");
|
||||||
|
if ( ! rules || ! rules.length )
|
||||||
|
return f.room_message(room, "This chat room does not have rules set.");
|
||||||
|
|
||||||
|
r.set("chatRules", rules);
|
||||||
|
r.set("shouldDisplayChatRules", true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.chat_commands.open_link = function(room, args) {
|
||||||
|
if ( ! args || ! args.length )
|
||||||
|
return "Usage: /open_link <url>";
|
||||||
|
|
||||||
|
var wnd = window.open(args.join(" "), "_blank");
|
||||||
|
wnd.opener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.chat_commands.fetch_link = function(room, args) {
|
||||||
|
if ( ! args || ! args.length )
|
||||||
|
return "Usage: /fetch_link <url> [template]\nTemplates use http://objectpath.org/ to format data. Default Template is \"Response: #$#\"";
|
||||||
|
|
||||||
|
var f = this,
|
||||||
|
url = args.shift(),
|
||||||
|
headers = {};
|
||||||
|
|
||||||
|
if ( /https?:\/\/[^.]+\.twitch\.tv\//.test(url) )
|
||||||
|
headers['Client-ID'] = constants.CLIENT_ID;
|
||||||
|
|
||||||
|
jQuery.ajax({
|
||||||
|
url: url,
|
||||||
|
headers: headers,
|
||||||
|
|
||||||
|
success: function(data) {
|
||||||
|
f.log("Response Received", data);
|
||||||
|
args = (args && args.length) ? args.join(" ").split(/#/g) : ["Response: ", "$"];
|
||||||
|
|
||||||
|
if ( typeof data === "string" )
|
||||||
|
data = [data];
|
||||||
|
|
||||||
|
var is_special = true,
|
||||||
|
output = [],
|
||||||
|
op = new ObjectPath(data);
|
||||||
|
|
||||||
|
for(var i=0; i < args.length; i++) {
|
||||||
|
var segment = args[i];
|
||||||
|
is_special = !is_special;
|
||||||
|
if ( ! is_special )
|
||||||
|
output.push(segment);
|
||||||
|
else
|
||||||
|
try {
|
||||||
|
output.push(format_result(op.execute(segment)));
|
||||||
|
} catch(err) {
|
||||||
|
f.log("Error", err);
|
||||||
|
output.push("[Error: " + (err.message || err) + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.room_message(room, output.join(''));
|
||||||
|
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
f.log("Request Error", xhr);
|
||||||
|
f.room_message(room, "Request Failed: " + (xhr.status === 0 ? 'Unknown Error. ' + (url.indexOf('https') === -1 ? 'Please make sure you\'re making HTTPS requests.' : 'Likely a CORS problem. Check your browser\'s Networking console for more.') : xhr.status + ' ' + (STATUS_CODES[xhr.status] || '' )));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------
|
// -----------------
|
||||||
// Mass Moderation
|
// Mass Moderation
|
||||||
// -----------------
|
// -----------------
|
||||||
|
|
|
@ -17,9 +17,16 @@ FFZ.prototype.setup_channel = function() {
|
||||||
// Settings stuff!
|
// Settings stuff!
|
||||||
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
|
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
|
||||||
document.body.classList.toggle('ffz-theater-stats', this.settings.theater_stats);
|
document.body.classList.toggle('ffz-theater-stats', this.settings.theater_stats);
|
||||||
|
document.body.classList.toggle('ffz-channel-bar-bottom', this.settings.channel_bar_bottom);
|
||||||
|
utils.toggle_cls('ffz-minimal-channel-title')(this.settings.channel_title_top === 2);
|
||||||
|
utils.toggle_cls('ffz-channel-title-top')(this.settings.channel_title_top > 0);
|
||||||
|
utils.toggle_cls('ffz-minimal-channel-bar')(this.settings.channel_bar_collapse);
|
||||||
|
|
||||||
this.log("Hooking the Ember Channel Index view.");
|
this.log("Hooking the Ember Channel Index redesign.");
|
||||||
if ( ! this.update_views('view:channel/index', this.modify_channel_index) )
|
this.update_views('component:channel-redesign', this.modify_channel_redesign);
|
||||||
|
|
||||||
|
this.log("Hooking the Ember Channel Index component.");
|
||||||
|
if ( ! this.update_views('component:legacy-channel', this.modify_channel_index) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.log("Hooking the Ember Channel model.");
|
this.log("Hooking the Ember Channel model.");
|
||||||
|
@ -71,52 +78,52 @@ FFZ.prototype.setup_channel = function() {
|
||||||
|
|
||||||
}.property('content.id', 'login.userData', 'login.userData.login'),*/
|
}.property('content.id', 'login.userData', 'login.userData.login'),*/
|
||||||
|
|
||||||
ffzUpdateUptime: function() {
|
/*ffzUpdateUptime: function() {
|
||||||
if ( f._cindex )
|
if ( f._cindex )
|
||||||
f._cindex.ffzUpdateUptime();
|
f._cindex.ffzUpdateUptime();
|
||||||
|
|
||||||
}.observes("isLive", "content.id"),
|
}.observes("isLive", "channel.id"),*/
|
||||||
|
|
||||||
ffzUpdateInfo: function() {
|
ffzUpdateInfo: function() {
|
||||||
if ( this._ffz_update_timer )
|
if ( this._ffz_update_timer )
|
||||||
clearTimeout(this._ffz_update_timer);
|
clearTimeout(this._ffz_update_timer);
|
||||||
|
|
||||||
if ( ! this.get('content.id') )
|
if ( ! this.get('channel.id') )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._ffz_update_timer = setTimeout(this.ffzCheckUpdate.bind(this), 55000 + (Math.random() * 10000));
|
this._ffz_update_timer = setTimeout(this.ffzCheckUpdate.bind(this), 55000 + (Math.random() * 10000));
|
||||||
}.observes("content.id"),
|
}.observes("channel.id"),
|
||||||
|
|
||||||
ffzCheckUpdate: function() {
|
ffzCheckUpdate: function() {
|
||||||
var t = this,
|
var t = this,
|
||||||
id = t.get('content.id');
|
id = t.get('channel.id');
|
||||||
|
|
||||||
id && utils.api.get("streams/" + id, {}, {version:3})
|
id && utils.api.get("streams/" + id, {}, {version:3})
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
if ( ! data || ! data.stream ) {
|
if ( ! data || ! data.stream ) {
|
||||||
// If the stream is offline, clear its created_at time and set it to zero viewers.
|
// If the stream is offline, clear its created_at time and set it to zero viewers.
|
||||||
t.set('content.stream.created_at', null);
|
t.set('channel.stream.createdAt', null);
|
||||||
t.set('content.stream.viewers', 0);
|
t.set('channel.stream.viewers', 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
t.set('content.stream.created_at', data.stream.created_at || null);
|
t.set('channel.stream.createdAt', utils.parse_date(data.stream.created_at) || null);
|
||||||
t.set('content.stream.viewers', data.stream.viewers || 0);
|
t.set('channel.stream.viewers', data.stream.viewers || 0);
|
||||||
|
|
||||||
var game = data.stream.game || (data.stream.channel && data.stream.channel.game);
|
var game = data.stream.game || (data.stream.channel && data.stream.channel.game);
|
||||||
if ( game ) {
|
if ( game ) {
|
||||||
t.set('content.game', game);
|
t.set('channel.game', game);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( data.stream.channel ) {
|
if ( data.stream.channel ) {
|
||||||
if ( data.stream.channel.status )
|
if ( data.stream.channel.status )
|
||||||
t.set('content.status', data.stream.channel.status);
|
t.set('channel.status', data.stream.channel.status);
|
||||||
|
|
||||||
if ( data.stream.channel.views )
|
if ( data.stream.channel.views )
|
||||||
t.set('content.views', data.stream.channel.views);
|
t.set('channel.views', data.stream.channel.views);
|
||||||
|
|
||||||
if ( data.stream.channel.followers && t.get('content.followers.isLoaded') )
|
if ( data.stream.channel.followers && t.get('channel.followers.isLoaded') )
|
||||||
t.set('content.followers.total', data.stream.channel.followers);
|
t.set('channel.followers.total', data.stream.channel.followers);
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -125,19 +132,19 @@ FFZ.prototype.setup_channel = function() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzUpdateTitle: function() {
|
/*ffzUpdateTitle: function() {
|
||||||
var name = this.get('content.name'),
|
var name = this.get('channel.name'),
|
||||||
display_name = this.get('content.display_name');
|
display_name = this.get('channel.display_name');
|
||||||
|
|
||||||
if ( display_name )
|
if ( display_name )
|
||||||
FFZ.capitalization[name] = [display_name, Date.now()];
|
FFZ.capitalization[name] = [display_name, Date.now()];
|
||||||
|
|
||||||
if ( f._cindex )
|
if ( f._cindex )
|
||||||
f._cindex.ffzFixTitle();
|
f._cindex.ffzFixTitle();
|
||||||
}.observes("content.status", "content.game", "content.id", "hostModeTarget.status", "hostModeTarget.id", "hostModeTarget.game"),
|
}.observes("channel.status", "channel.game", "channel.id", "channel.hostModeTarget.status", "channel.hostModeTarget.id", "channel.hostModeTarget.game"),*/
|
||||||
|
|
||||||
ffzHostTarget: function() {
|
ffzHostTarget: function() {
|
||||||
var target = this.get('content.hostModeTarget'),
|
var target = this.get('channel.hostModeTarget'),
|
||||||
name = target && target.get('name'),
|
name = target && target.get('name'),
|
||||||
id = target && target.get('id'),
|
id = target && target.get('id'),
|
||||||
display_name = target && target.get('display_name');
|
display_name = target && target.get('display_name');
|
||||||
|
@ -165,10 +172,9 @@ FFZ.prototype.setup_channel = function() {
|
||||||
if ( f.settings.srl_races )
|
if ( f.settings.srl_races )
|
||||||
f.rebuild_race_ui();
|
f.rebuild_race_ui();
|
||||||
|
|
||||||
}.observes("content.hostModeTarget")
|
}.observes("channel.hostModeTarget")
|
||||||
});
|
});
|
||||||
|
|
||||||
Ember.propertyDidChange(Channel, 'isEditable');
|
|
||||||
Channel.ffzUpdateInfo();
|
Channel.ffzUpdateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,17 +197,345 @@ FFZ.prototype._modify_cmodel = function(model) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype.modify_channel_redesign = function(view) {
|
||||||
|
var f = this;
|
||||||
|
utils.ember_reopen_view(view, {
|
||||||
|
ffz_init: function() {
|
||||||
|
var channel_id = this.get("channel.id"),
|
||||||
|
el = this.get("element");
|
||||||
|
|
||||||
|
f._cindex = this;
|
||||||
|
f.ws_send("sub", "channel." + channel_id);
|
||||||
|
|
||||||
|
el.setAttribute('data-channel', channel_id);
|
||||||
|
el.classList.add('ffz-channel');
|
||||||
|
|
||||||
|
this.ffzFixTitle();
|
||||||
|
this.ffzUpdateUptime();
|
||||||
|
this.ffzUpdateChatters();
|
||||||
|
this.ffzUpdateHostButton();
|
||||||
|
this.ffzUpdatePlayerStats();
|
||||||
|
this.ffzUpdateCoverHeight();
|
||||||
|
|
||||||
|
if ( f.settings.auto_theater ) {
|
||||||
|
var player = f.players && f.players[channel_id] && f.players[channel_id].get('player');
|
||||||
|
if ( player )
|
||||||
|
player.setTheatre(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$().on("click", ".ffz-creative-tag-link", function(e) {
|
||||||
|
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
|
||||||
|
return;
|
||||||
|
|
||||||
|
utils.ember_lookup("router:main").transitionTo('creative.hashtag.index', this.getAttribute('data-tag'));
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdateCoverHeight: function() {
|
||||||
|
var old_height = this.get('channelCoverHeight'),
|
||||||
|
new_height = f.settings.channel_bar_bottom ? 0 : 380;
|
||||||
|
|
||||||
|
this.set('channelCoverHeight', new_height);
|
||||||
|
this.$("#channel").toggleClass('ffz-bar-fixed', this.get('isFixed'));
|
||||||
|
|
||||||
|
if ( old_height !== new_height )
|
||||||
|
this.scrollTo(this.$scrollContainer.scrollTop() + (new_height - old_height));
|
||||||
|
|
||||||
|
}.observes('isFixed'),
|
||||||
|
|
||||||
|
ffzFixTitle: function() {
|
||||||
|
if ( ! f.settings.stream_title )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var channel_id = this.get("channel.id"),
|
||||||
|
status = this.get("channel.status"),
|
||||||
|
game = this.get("channel.game"),
|
||||||
|
|
||||||
|
tokens = f.tokenize_line(channel_id, channel_id, status, true);
|
||||||
|
|
||||||
|
if ( game === 'Creative' )
|
||||||
|
tokens = f.tokenize_ctags(tokens);
|
||||||
|
|
||||||
|
var el = this.$(".cn-metabar__title .card__title");
|
||||||
|
el && el.html(f.render_tokens(tokens));
|
||||||
|
}.observes('channel.id', 'channel.status', 'channel.game'),
|
||||||
|
|
||||||
|
ffzUpdateUptime: function() {
|
||||||
|
if ( this._ffz_update_uptime ) {
|
||||||
|
clearTimeout(this._ffz_update_uptime);
|
||||||
|
delete this._ffz_update_uptime;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = this.get('element');
|
||||||
|
if ( this.isDestroyed || ! container || ! f.settings.stream_uptime || ! this.get('isLiveAccordingToKraken') )
|
||||||
|
return container && this.$("#ffz-uptime-display").remove();
|
||||||
|
|
||||||
|
// Schedule an update.
|
||||||
|
this._ffz_update_uptime = setTimeout(this.ffzUpdateUptime.bind(this), 1000);
|
||||||
|
|
||||||
|
// Determine when the channel last went live.
|
||||||
|
var online = this.get("channel.stream.createdAt"),
|
||||||
|
now = Date.now() - (f._ws_server_offset || 0);
|
||||||
|
|
||||||
|
var uptime = online && Math.floor((now - online.getTime()) / 1000) || -1;
|
||||||
|
if ( uptime < 0 )
|
||||||
|
return this.$("#ffz-uptime-display").remove();
|
||||||
|
|
||||||
|
var el = container.querySelector('#ffz-uptime-display span');
|
||||||
|
if ( ! el ) {
|
||||||
|
var cont = container.querySelector('.cn-metabar__more');
|
||||||
|
if ( ! cont )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stat = utils.createElement('span'),
|
||||||
|
figure = utils.createElement('figure', 'icon cn-metabar__icon', constants.CLOCK + ' '),
|
||||||
|
balloon = utils.createElement('div', 'balloon balloon--tooltip balloon--down balloon--center'),
|
||||||
|
balloon_wrapper = utils.createElement('div', 'balloon-wrapper', figure),
|
||||||
|
stat_wrapper = utils.createElement('div', 'cn-metabar__ffz flex__item mg-l-1', balloon_wrapper);
|
||||||
|
|
||||||
|
balloon_wrapper.appendChild(stat);
|
||||||
|
balloon_wrapper.appendChild(balloon);
|
||||||
|
|
||||||
|
stat_wrapper.id = 'ffz-uptime-display';
|
||||||
|
balloon.innerHTML = 'Stream Uptime <nobr>(since ' + online.toLocaleString() + ')</nobr>';
|
||||||
|
|
||||||
|
var viewers = cont.querySelector(".cn-metabar__livecount");
|
||||||
|
if ( viewers )
|
||||||
|
cont.insertBefore(stat_wrapper, viewers.nextSibling);
|
||||||
|
else
|
||||||
|
cont.appendChild(stat_wrapper);
|
||||||
|
|
||||||
|
el = stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.innerHTML = utils.time_to_string(uptime, false, false, false, f.settings.stream_uptime === 1 || f.settings.stream_uptime === 3);
|
||||||
|
}.observes('channel.stream.createdAt', 'isLiveAccordingToKraken'),
|
||||||
|
|
||||||
|
ffzUpdatePlayerStats: function() {
|
||||||
|
if ( this._ffz_update_stats ) {
|
||||||
|
clearTimeout(this._ffz_update_stats);
|
||||||
|
this._ffz_update_stats = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop scheduling this so it can die.
|
||||||
|
if ( this.isDestroyed )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Schedule an update.
|
||||||
|
if ( f.settings.player_stats )
|
||||||
|
this._ffz_update_stats = setTimeout(this.ffzUpdatePlayerStats.bind(this), 1000);
|
||||||
|
|
||||||
|
var channel_id = this.get("channel.id"),
|
||||||
|
container = this.get("element"),
|
||||||
|
player_cont = f.players && f.players[channel_id],
|
||||||
|
player, stats;
|
||||||
|
|
||||||
|
try {
|
||||||
|
player = player_cont && player_cont.get('player');
|
||||||
|
stats = player && player.getVideoInfo();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("Channel ffzUpdatePlayerStats: player.getVideoInfo", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! container || ! f.settings.player_stats || ! stats || ! stats.hls_latency_broadcaster )
|
||||||
|
return container && this.$("#ffz-player-stats").remove();
|
||||||
|
|
||||||
|
var el = container.querySelector("#ffz-player-stats");
|
||||||
|
if ( ! el ) {
|
||||||
|
var cont = container.querySelector('.cn-metabar__more');
|
||||||
|
if ( ! cont )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stat = utils.createElement('span'),
|
||||||
|
figure = utils.createElement('figure', 'icon cn-metabar__icon', constants.GRAPH + ' '),
|
||||||
|
balloon = utils.createElement('div', 'balloon balloon--tooltip balloon--up balloon--center'),
|
||||||
|
balloon_wrapper = utils.createElement('div', 'balloon-wrapper', figure);
|
||||||
|
|
||||||
|
el = utils.createElement('div', 'cn-metabar__ffz flex__item mg-l-1', balloon_wrapper);
|
||||||
|
|
||||||
|
balloon_wrapper.appendChild(stat);
|
||||||
|
balloon_wrapper.appendChild(balloon);
|
||||||
|
|
||||||
|
el.id = 'ffz-player-stats';
|
||||||
|
|
||||||
|
var viewers = cont.querySelector('#ffz-uptime-display') || cont.querySelector(".cn-metabar__livecount");
|
||||||
|
if ( viewers )
|
||||||
|
cont.insertBefore(el, viewers.nextSibling);
|
||||||
|
else
|
||||||
|
cont.appendChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat = el.querySelector('span'),
|
||||||
|
balloon = el.querySelector('.balloon');
|
||||||
|
|
||||||
|
var delay = Math.round(stats.hls_latency_broadcaster / 10) / 100,
|
||||||
|
dropped = utils.number_commas(stats.dropped_frames || 0),
|
||||||
|
bitrate;
|
||||||
|
|
||||||
|
if ( stats.playback_bytes_per_second )
|
||||||
|
bitrate = Math.round(stats.playback_bytes_per_second * 8 / 10.24) / 100;
|
||||||
|
else
|
||||||
|
bitrate = Math.round(stats.current_bitrate * 100) / 100;
|
||||||
|
|
||||||
|
var is_old = delay > 180;
|
||||||
|
if ( is_old ) {
|
||||||
|
delay = Math.floor(delay);
|
||||||
|
stat.textContent = utils.time_to_string(delay, true, delay > 172800) + ' old';
|
||||||
|
} else {
|
||||||
|
delay = delay.toString();
|
||||||
|
var ind = delay.indexOf('.');
|
||||||
|
delay += (ind === -1 ? '.00' : (ind >= delay.length - 2 ? '0' : '')) + 's';
|
||||||
|
stat.textContent = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
balloon.innerHTML = (is_old ? 'Video Information<br>' +
|
||||||
|
'Broadcast ' + utils.time_to_string(delay, true) + ' Ago<br><br>' : 'Stream Latency<br>') +
|
||||||
|
'Video: ' + stats.vid_width + 'x' + stats.vid_height + 'p ' + stats.current_fps + ' fps<br>' +
|
||||||
|
'Playback Rate: ' + bitrate + ' Kbps<br>' +
|
||||||
|
'Dropped Frames: ' + dropped;
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdateChatters: function() {
|
||||||
|
var channel_id = this.get("channel.id"),
|
||||||
|
room = f.rooms && f.rooms[channel_id],
|
||||||
|
container = this.get('element');
|
||||||
|
|
||||||
|
if ( ! container || ! room || ! f.settings.chatter_count )
|
||||||
|
return container && this.$("#ffz-chatter-display").remove();
|
||||||
|
|
||||||
|
var chatter_count = Object.keys(room.room.get('ffz_chatters') || {}).length,
|
||||||
|
el = container.querySelector('#ffz-chatter-display span');
|
||||||
|
|
||||||
|
if ( ! el ) {
|
||||||
|
var cont = container.querySelector('.cn-metabar__more');
|
||||||
|
if ( ! cont )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var stat = utils.createElement('span'),
|
||||||
|
figure = utils.createElement('figure', 'icon cn-metabar__icon', constants.ROOMS + ' '),
|
||||||
|
balloon = utils.createElement('div', 'balloon balloon--tooltip balloon--down balloon--center', 'Currently in Chat'),
|
||||||
|
balloon_wrapper = utils.createElement('div', 'balloon-wrapper', figure),
|
||||||
|
stat_wrapper = utils.createElement('div', 'cn-metabar__ffz flex__item mg-l-1', balloon_wrapper);
|
||||||
|
|
||||||
|
balloon_wrapper.appendChild(stat);
|
||||||
|
balloon_wrapper.appendChild(balloon);
|
||||||
|
|
||||||
|
stat_wrapper.id = 'ffz-chatter-display';
|
||||||
|
|
||||||
|
var viewers = cont.querySelector('#ffz-player-stats') || cont.querySelector('#ffz-uptime-display') || cont.querySelector(".cn-metabar__livecount") || cont.querySelector(".cn-metabar__viewcount");
|
||||||
|
if ( viewers )
|
||||||
|
cont.insertBefore(stat_wrapper, viewers.nextSibling);
|
||||||
|
else
|
||||||
|
cont.appendChild(stat_wrapper);
|
||||||
|
|
||||||
|
el = stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.innerHTML = utils.number_commas(chatter_count);
|
||||||
|
}.observes('channel.id'),
|
||||||
|
|
||||||
|
ffzUpdateHostButton: function() {
|
||||||
|
var t = this,
|
||||||
|
channel_id = this.get("channel.id"),
|
||||||
|
hosted_id = this.get("channel.hostModeTarget.id"),
|
||||||
|
|
||||||
|
user = f.get_user(),
|
||||||
|
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||||
|
now_hosting = room && room.ffz_host_target,
|
||||||
|
hosts_left = room && room.ffz_hosts_left,
|
||||||
|
|
||||||
|
el = this.get("element"),
|
||||||
|
|
||||||
|
update_button = function(channel, container, before) {
|
||||||
|
if ( ! container )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var btn = container.querySelector('#ffz-ui-host-button');
|
||||||
|
|
||||||
|
if ( ! f.settings.stream_host_button || ! user || user.login === channel ) {
|
||||||
|
if ( btn )
|
||||||
|
btn.parentElement.removeChild(btn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! btn ) {
|
||||||
|
btn = utils.createElement('button', 'button button--hollow mg-l-1'),
|
||||||
|
|
||||||
|
btn.id = 'ffz-ui-host-button';
|
||||||
|
btn.addEventListener('click', t.ffzClickHost.bind(t, channel !== channel_id));
|
||||||
|
|
||||||
|
if ( before )
|
||||||
|
container.insertBefore(btn, before);
|
||||||
|
else
|
||||||
|
container.appendChild(btn);
|
||||||
|
|
||||||
|
jQuery(btn).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.classList.remove('disabled');
|
||||||
|
btn.innerHTML = channel === now_hosting ? 'Unhost' : 'Host';
|
||||||
|
if ( now_hosting ) {
|
||||||
|
var name = FFZ.get_capitalization(now_hosting);
|
||||||
|
btn.title = 'You are now hosting ' + f.format_display_name(name, now_hosting, true)[0] + '.';
|
||||||
|
} else
|
||||||
|
btn.title = 'You are not hosting any channel.';
|
||||||
|
|
||||||
|
if ( typeof hosts_left === 'number' )
|
||||||
|
btn.title += ' You have ' + hosts_left + ' host command' + utils.pluralize(hosts_left) + ' remaining this half hour.';
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( ! el )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.set("ffz_host_updating", false);
|
||||||
|
|
||||||
|
if ( channel_id ) {
|
||||||
|
var container = el.querySelector('.cn-metabar__more'),
|
||||||
|
share = container && container.querySelector('.js-share-box');
|
||||||
|
|
||||||
|
update_button(channel_id, container, share ? share.parentElement : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hosted_id )
|
||||||
|
update_button(hosted_id, el.querySelector('.cn-hosting--bottom'));
|
||||||
|
}.observes('channel.id', 'channel.hostModeTarget.id'),
|
||||||
|
|
||||||
|
ffzClickHost: function(is_host, e) {
|
||||||
|
var btn = e.target,
|
||||||
|
target = this.get(is_host ? 'channel.hostModeTarget.id' : 'channel.id'),
|
||||||
|
user = f.get_user(),
|
||||||
|
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||||
|
now_hosting = room && room.ffz_host_target;
|
||||||
|
|
||||||
|
if ( ! room || this.get('ffz_host_updating') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
btn.classList.add('disabled');
|
||||||
|
btn.title = 'Updating...';
|
||||||
|
|
||||||
|
this.set('ffz_host_updating', true);
|
||||||
|
if ( now_hosting === target )
|
||||||
|
room.send('/unhost', true);
|
||||||
|
else
|
||||||
|
room.send('/host ' + target, true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.modify_channel_index = function(view) {
|
FFZ.prototype.modify_channel_index = function(view) {
|
||||||
var f = this;
|
var f = this;
|
||||||
utils.ember_reopen_view(view, {
|
utils.ember_reopen_view(view, {
|
||||||
ffz_init: function() {
|
ffz_init: function() {
|
||||||
var id = this.get('controller.content.id') || this.get('controller.id'),
|
var channel_id = this.get('model.id'),
|
||||||
el = this.get('element');
|
el = this.get('element');
|
||||||
|
|
||||||
f._cindex = this;
|
f._cindex = this;
|
||||||
f.ws_send("sub", "channel." + id);
|
f.ws_send("sub", "channel." + channel_id);
|
||||||
|
|
||||||
el.setAttribute('data-channel', id);
|
el.setAttribute('data-channel', channel_id);
|
||||||
el.classList.add('ffz-channel');
|
el.classList.add('ffz-channel');
|
||||||
|
|
||||||
this.ffzFixTitle();
|
this.ffzFixTitle();
|
||||||
|
@ -225,7 +559,7 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
f.rebuild_race_ui();
|
f.rebuild_race_ui();
|
||||||
|
|
||||||
if ( f.settings.auto_theater ) {
|
if ( f.settings.auto_theater ) {
|
||||||
var player = f.players && f.players[id] && f.players[id].get('player');
|
var player = f.players && f.players[channel_id] && f.players[channel_id].get('player');
|
||||||
if ( player )
|
if ( player )
|
||||||
player.setTheatre(true);
|
player.setTheatre(true);
|
||||||
}
|
}
|
||||||
|
@ -241,12 +575,15 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ffz_destroy: function() {
|
ffz_destroy: function() {
|
||||||
var id = this.get('controller.content.id') || this.get('controller.id');
|
var channel_id = this.get('model.id');
|
||||||
if ( id )
|
if ( channel_id )
|
||||||
f.ws_send("unsub", "channel." + id);
|
f.ws_send("unsub", "channel." + channel_id);
|
||||||
|
|
||||||
this.get('element').setAttribute('data-channel', '');
|
this.get('element').setAttribute('data-channel', '');
|
||||||
f._cindex = undefined;
|
|
||||||
|
if ( f._cindex === this )
|
||||||
|
f._cindex = null;
|
||||||
|
|
||||||
if ( this._ffz_update_uptime )
|
if ( this._ffz_update_uptime )
|
||||||
clearTimeout(this._ffz_update_uptime);
|
clearTimeout(this._ffz_update_uptime);
|
||||||
|
|
||||||
|
@ -259,7 +596,7 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.remove('ffz-small-player');
|
document.body.classList.remove('ffz-small-player');
|
||||||
utils.update_css(f._channel_style, id, null);
|
utils.update_css(f._channel_style, channel_id, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -279,23 +616,23 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
if ( f.has_bttv || ! f.settings.stream_title )
|
if ( f.has_bttv || ! f.settings.stream_title )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var status = this.get("controller.content.status"),
|
var channel_id = this.get('model.id'),
|
||||||
channel = this.get("controller.content.id"),
|
status = this.get('model.status'),
|
||||||
game = this.get("controller.content.game"),
|
game = this.get('model.game'),
|
||||||
|
|
||||||
tokens = f.tokenize_line(channel, channel, status, true);
|
tokens = f.tokenize_line(channel_id, channel_id, status, true);
|
||||||
|
|
||||||
if ( game === 'Creative' )
|
if ( game === 'Creative' )
|
||||||
tokens = f.tokenize_ctags(tokens);
|
tokens = f.tokenize_ctags(tokens);
|
||||||
|
|
||||||
this.$("#broadcast-meta .title").html(f.render_tokens(tokens));
|
this.$("#broadcast-meta .title").html(f.render_tokens(tokens));
|
||||||
|
|
||||||
status = this.get('controller.hostModeTarget.status');
|
status = this.get('hostModeTarget.status');
|
||||||
channel = this.get('controller.hostModeTarget.id');
|
channel_id = this.get('hostModeTarget.id');
|
||||||
game = this.get('controller.hostModeTarget.game');
|
game = this.get('hostModeTarget.game');
|
||||||
|
|
||||||
if ( channel ) {
|
if ( channel_id ) {
|
||||||
tokens = f.tokenize_line(channel, channel, status, true);
|
tokens = f.tokenize_line(channel_id, channel_id, status, true);
|
||||||
if ( game === 'Creative' )
|
if ( game === 'Creative' )
|
||||||
tokens = f.tokenize_ctags(tokens);
|
tokens = f.tokenize_ctags(tokens);
|
||||||
|
|
||||||
|
@ -305,8 +642,8 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
|
|
||||||
|
|
||||||
ffzUpdateHostButton: function() {
|
ffzUpdateHostButton: function() {
|
||||||
var channel_id = this.get('controller.content.id') || this.get('controller.id'),
|
var channel_id = this.get('model.id'),
|
||||||
hosted_id = this.get('controller.hostModeTarget.id'),
|
hosted_id = this.get('hostModeTarget.id'),
|
||||||
|
|
||||||
user = f.get_user(),
|
user = f.get_user(),
|
||||||
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||||
|
@ -399,7 +736,7 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
|
|
||||||
ffzClickHost: function(is_host, e) {
|
ffzClickHost: function(is_host, e) {
|
||||||
var btn = e.target,
|
var btn = e.target,
|
||||||
target = is_host ? this.get('controller.hostModeTarget.id') : (this.get('controller.content.id') || this.get('controller.id')),
|
target = is_host ? this.get('hostModeTarget.id') : this.get('model.id'),
|
||||||
user = f.get_user(),
|
user = f.get_user(),
|
||||||
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||||
now_hosting = room && room.ffz_host_target;
|
now_hosting = room && room.ffz_host_target;
|
||||||
|
@ -420,7 +757,7 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
|
|
||||||
ffzUpdateChatters: function() {
|
ffzUpdateChatters: function() {
|
||||||
// Get the counts.
|
// Get the counts.
|
||||||
var room_id = this.get('controller.content.id') || this.get('controller.id'),
|
var room_id = this.get('model.id'),
|
||||||
room = f.rooms && f.rooms[room_id];
|
room = f.rooms && f.rooms[room_id];
|
||||||
|
|
||||||
if ( ! room || ! f.settings.chatter_count ) {
|
if ( ! room || ! f.settings.chatter_count ) {
|
||||||
|
@ -505,8 +842,8 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
if ( f.settings.player_stats )
|
if ( f.settings.player_stats )
|
||||||
this._ffz_update_stats = setTimeout(this.ffzUpdatePlayerStats.bind(this), 1000);
|
this._ffz_update_stats = setTimeout(this.ffzUpdatePlayerStats.bind(this), 1000);
|
||||||
|
|
||||||
var channel_id = this.get('controller.content.id') || this.get('controller.id'),
|
var channel_id = this.get('model.id'),
|
||||||
hosted_id = this.get('controller.hostModeTarget.id'),
|
hosted_id = this.get('hostModeTarget.id'),
|
||||||
|
|
||||||
el = this.get('element');
|
el = this.get('element');
|
||||||
|
|
||||||
|
@ -667,7 +1004,8 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
delete this._ffz_update_uptime;
|
delete this._ffz_update_uptime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! f.settings.stream_uptime || ! this.get("controller.isLiveAccordingToKraken") ) {
|
var controller = utils.ember_lookup('controller:channel');
|
||||||
|
if ( ! f.settings.stream_uptime || ! (controller && controller.get('isLiveAccordingToKraken')) ) {
|
||||||
var el = this.get('element').querySelector('#ffz-uptime-display');
|
var el = this.get('element').querySelector('#ffz-uptime-display');
|
||||||
if ( el )
|
if ( el )
|
||||||
el.parentElement.removeChild(el);
|
el.parentElement.removeChild(el);
|
||||||
|
@ -678,7 +1016,7 @@ FFZ.prototype.modify_channel_index = function(view) {
|
||||||
this._ffz_update_uptime = setTimeout(this.ffzUpdateUptime.bind(this), 1000);
|
this._ffz_update_uptime = setTimeout(this.ffzUpdateUptime.bind(this), 1000);
|
||||||
|
|
||||||
// Determine when the channel last went live.
|
// Determine when the channel last went live.
|
||||||
var online = this.get("controller.content.stream.created_at"),
|
var online = this.get("model.stream.created_at"),
|
||||||
now = Date.now() - (f._ws_server_offset || 0);
|
now = Date.now() - (f._ws_server_offset || 0);
|
||||||
|
|
||||||
online = online && utils.parse_date(online);
|
online = online && utils.parse_date(online);
|
||||||
|
@ -894,6 +1232,93 @@ FFZ.settings_info.stream_title = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.channel_bar_bottom = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
no_bttv: true,
|
||||||
|
no_mobile: true,
|
||||||
|
|
||||||
|
category: "Appearance",
|
||||||
|
name: "Channel Bar on Bottom",
|
||||||
|
help: "Hide the profile banner and position the channel bar at the bottom of the screen.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( this.has_bttv )
|
||||||
|
return;
|
||||||
|
|
||||||
|
utils.toggle_cls('ffz-channel-bar-bottom')(val);
|
||||||
|
if ( this._cindex )
|
||||||
|
this._cindex.ffzUpdateCoverHeight();
|
||||||
|
|
||||||
|
var Layout = utils.ember_lookup('service:layout');
|
||||||
|
if ( Layout )
|
||||||
|
Ember.propertyDidChange(Layout, 'windowHeight');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.channel_bar_collapse = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
no_bttv: true,
|
||||||
|
no_mobile: true,
|
||||||
|
|
||||||
|
category: "Appearance",
|
||||||
|
name: "Minimal Channel Bar",
|
||||||
|
help: "Slide the channel bar mostly out of view when it's not being used.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( this.has_bttv )
|
||||||
|
return;
|
||||||
|
|
||||||
|
utils.toggle_cls('ffz-minimal-channel-bar')(val);
|
||||||
|
|
||||||
|
var Layout = utils.ember_lookup('service:layout');
|
||||||
|
if ( Layout )
|
||||||
|
Ember.propertyDidChange(Layout, 'windowHeight');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.channel_title_top = {
|
||||||
|
type: "select",
|
||||||
|
options: {
|
||||||
|
0: "Disabled",
|
||||||
|
1: "On Top",
|
||||||
|
2: "On Top, Minimal"
|
||||||
|
},
|
||||||
|
|
||||||
|
value: 0,
|
||||||
|
process_value: function(val) {
|
||||||
|
if ( typeof val === "string" ) {
|
||||||
|
val = parseInt(val);
|
||||||
|
if ( isNaN(val) || ! isFinite(val) )
|
||||||
|
val = 0;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
|
||||||
|
no_bttv: true,
|
||||||
|
no_mobile: true,
|
||||||
|
|
||||||
|
category: "Appearance",
|
||||||
|
name: "Channel Title on Top",
|
||||||
|
help: "Display the channel title and game above the player rather than below.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( this.has_bttv )
|
||||||
|
return;
|
||||||
|
|
||||||
|
document.body.classList.toggle('ffz-minimal-channel-title', val === 2);
|
||||||
|
document.body.classList.toggle('ffz-channel-title-top', val > 0);
|
||||||
|
|
||||||
|
var Layout = utils.ember_lookup('service:layout');
|
||||||
|
if ( Layout )
|
||||||
|
Ember.propertyDidChange(Layout, 'windowHeight');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.theater_stats = {
|
FFZ.settings_info.theater_stats = {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: true,
|
value: true,
|
||||||
|
|
|
@ -100,7 +100,7 @@ FFZ.settings_info.input_complete_emotes = {
|
||||||
2: "All Emoticons"
|
2: "All Emoticons"
|
||||||
},
|
},
|
||||||
|
|
||||||
value: 0,
|
value: 1,
|
||||||
|
|
||||||
process_value: function(val) {
|
process_value: function(val) {
|
||||||
if ( typeof val === 'string' )
|
if ( typeof val === 'string' )
|
||||||
|
@ -111,7 +111,7 @@ FFZ.settings_info.input_complete_emotes = {
|
||||||
category: "Chat Input",
|
category: "Chat Input",
|
||||||
no_bttv: true,
|
no_bttv: true,
|
||||||
|
|
||||||
name: "Tab-Complete Emoticons <span>Beta</span>",
|
name: "Tab-Complete Emoticons",
|
||||||
help: "Use tab completion to complete emoticon names in chat.",
|
help: "Use tab completion to complete emoticon names in chat.",
|
||||||
|
|
||||||
on_update: function(val) {
|
on_update: function(val) {
|
||||||
|
@ -503,7 +503,7 @@ FFZ.prototype.modify_chat_input = function(component) {
|
||||||
|
|
||||||
ffzFetchNameSuggestions: function() {
|
ffzFetchNameSuggestions: function() {
|
||||||
if ( ! this.get('ffz_suggestions_visible') )
|
if ( ! this.get('ffz_suggestions_visible') )
|
||||||
this.set('ffz_name_suggestions', this.get('suggestions'));
|
this.set('ffz_name_suggestions', this.get('suggestions')());
|
||||||
}.observes('suggestions'),
|
}.observes('suggestions'),
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -138,16 +138,17 @@ FFZ.settings_info.chat_batching = {
|
||||||
FFZ.settings_info.chat_delay = {
|
FFZ.settings_info.chat_delay = {
|
||||||
type: "select",
|
type: "select",
|
||||||
options: {
|
options: {
|
||||||
"-1": "Default Delay (Room Specific; Non-Mod Only)",
|
"-1": ["Default Delay (Room Specific; Non-Mod Only)", 0],
|
||||||
0: "No Delay",
|
0: ["No Delay", 1],
|
||||||
300: "Minor (Bot Moderation; 0.3s)",
|
300: ["Minor (Bot Moderation; 0.3s)", 2],
|
||||||
1200: "Normal (Human Moderation; 1.2s)",
|
1200: ["Normal (Human Moderation; 1.2s)", 3],
|
||||||
5000: "Large (Spoiler Removal / Really Slow Mods; 5s)",
|
5000: ["Large (Spoiler Removal / Really Slow Mods; 5s)", 4],
|
||||||
10000: "Extra Large (10s)",
|
10000: ["Extra Large (10s)", 5],
|
||||||
15000: "Extremely Large (15s)",
|
15000: ["Extremely Large (15s)", 6],
|
||||||
20000: "Mods Asleep; Delay Chat (20s)",
|
20000: ["Mods Asleep; Delay Chat (20s)", 7],
|
||||||
30000: "Half a Minute (30s)",
|
30000: ["Half a Minute (30s)", 8],
|
||||||
60000: "Why??? (1m)"
|
60000: ["Why??? (1m)", 9],
|
||||||
|
788400000000: ["The CBenni Option (Literally 25 Years)", 10]
|
||||||
},
|
},
|
||||||
value: -1,
|
value: -1,
|
||||||
|
|
||||||
|
@ -484,9 +485,83 @@ FFZ.prototype.setup_chatview = function() {
|
||||||
|
|
||||||
this.log("Hooking the Ember Chat view.");
|
this.log("Hooking the Ember Chat view.");
|
||||||
this.update_views('view:chat', this.modify_chat_view);
|
this.update_views('view:chat', this.modify_chat_view);
|
||||||
|
|
||||||
|
this.log("Hooking the Ember from-display-preview component.");
|
||||||
|
this.update_views('component:chat/from-display-preview', this.modify_from_display_preview);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------
|
||||||
|
// Modify From Display Preview
|
||||||
|
// ----------------------------
|
||||||
|
|
||||||
|
FFZ.prototype.modify_from_display_preview = function(view) {
|
||||||
|
var f = this;
|
||||||
|
utils.ember_reopen_view(view, {
|
||||||
|
attributeBindings: ["chatUser.id:data-room"],
|
||||||
|
|
||||||
|
ffz_init: function() {
|
||||||
|
var el = this.get('element');
|
||||||
|
if ( el )
|
||||||
|
el.classList.add('from-display-preview');
|
||||||
|
|
||||||
|
//this.ffzUpdateChatColor();
|
||||||
|
this.ffzRenderBadges();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffz_badges: function() {
|
||||||
|
var badges = f.get_twitch_badges(this.get('chatUser.chatBadges'));
|
||||||
|
return f.get_badges(this.get('userData.login'), this.get('chatUser.id'), badges);
|
||||||
|
}.property('chatUser.chatBadges', 'userData.login', 'chatUser.id'),
|
||||||
|
|
||||||
|
/*ffzUpdateChatColor: function() {
|
||||||
|
var el = this.get('element'),
|
||||||
|
name = el && el.querySelector('span.strong');
|
||||||
|
|
||||||
|
if ( ! name )
|
||||||
|
return;
|
||||||
|
|
||||||
|
name.classList.add('has-color');
|
||||||
|
name.classList.add('replay-color');
|
||||||
|
name.setAttribute('data-color', this.get('room.model.chatColor'));
|
||||||
|
|
||||||
|
}.observes('room.model.chatColor'),*/
|
||||||
|
|
||||||
|
ffzRenderBadges: function() {
|
||||||
|
var badges = this.get('ffz_badges'),
|
||||||
|
el = this.get('element'),
|
||||||
|
badge_container = el && el.querySelector('.ffz_badges');
|
||||||
|
|
||||||
|
if ( ! badge_container ) {
|
||||||
|
var old_container = el && el.querySelector('.badges');
|
||||||
|
if ( ! old_container )
|
||||||
|
return;
|
||||||
|
|
||||||
|
old_container.classList.add('hidden');
|
||||||
|
|
||||||
|
badge_container = utils.createElement('div', 'badges ffz_badges');
|
||||||
|
old_container.parentElement.insertBefore(badge_container, old_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
badge_container.innerHTML = f.render_badges(badges);
|
||||||
|
|
||||||
|
}.observes('ffz_badges'),
|
||||||
|
|
||||||
|
/*colorStyle: function(e) {
|
||||||
|
var base_color = this.get('room.model.chatColor'),
|
||||||
|
colors = f._handle_color(base_color);
|
||||||
|
|
||||||
|
if ( ! colors )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return "color:" + ( f.settings.dark_twitch ? colors[1] : colors[0] );
|
||||||
|
}*/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
// Modify Chat View
|
// Modify Chat View
|
||||||
// --------------------
|
// --------------------
|
||||||
|
@ -662,7 +737,7 @@ FFZ.prototype.modify_chat_view = function(view) {
|
||||||
ffzUpdateHost: function() {
|
ffzUpdateHost: function() {
|
||||||
var Channel = utils.ember_lookup('controller:channel'),
|
var Channel = utils.ember_lookup('controller:channel'),
|
||||||
Room = utils.ember_resolve('model:room'),
|
Room = utils.ember_resolve('model:room'),
|
||||||
target = Room && Channel && Channel.get('hostModeTarget'),
|
target = Room && Channel && Channel.get('channelModel.hostModeTarget'),
|
||||||
|
|
||||||
updated = false;
|
updated = false;
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,83 @@ FFZ.prototype.setup_profile_following = function() {
|
||||||
|
|
||||||
// Modify followed items.
|
// Modify followed items.
|
||||||
this.update_views('component:display-followed-item', this.modify_display_followed_item);
|
this.update_views('component:display-followed-item', this.modify_display_followed_item);
|
||||||
|
this.update_views('component:twitch-profile-card', this.modify_twitch_profile_card);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype.modify_twitch_profile_card = function(component) {
|
||||||
|
var f = this;
|
||||||
|
utils.ember_reopen_view(component, {
|
||||||
|
ffzParentModel: function() {
|
||||||
|
var x = this.get('parentView');
|
||||||
|
while(x) {
|
||||||
|
var model = x.get('model');
|
||||||
|
if ( model )
|
||||||
|
return model;
|
||||||
|
x = x.get('parentView');
|
||||||
|
}
|
||||||
|
}.property('parentView'),
|
||||||
|
|
||||||
|
ffz_init: function() {
|
||||||
|
var el = this.get('element');
|
||||||
|
|
||||||
|
el.classList.add('ffz-processed');
|
||||||
|
jQuery('.aspect', el).tipsy();
|
||||||
|
|
||||||
|
if ( ! f.settings.enhance_profile_following )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ffzUpdate();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdate: function() {
|
||||||
|
var el = this.get('element'),
|
||||||
|
t_el = el.querySelector('.ffz-followed-since'),
|
||||||
|
|
||||||
|
channel_id = this.get('ffzParentModel.model.id'),
|
||||||
|
is_following = this.get('ffzParentModel.relationshipName') === 'following',
|
||||||
|
|
||||||
|
user = f.get_user(),
|
||||||
|
mine = user && user.login && user.login === channel_id,
|
||||||
|
big_cache = is_following ? f._following_cache : f._follower_cache,
|
||||||
|
user_cache = big_cache[channel_id] = big_cache[channel_id] || {},
|
||||||
|
|
||||||
|
user_id = this.get('channelInfo.id'),
|
||||||
|
data = user_cache[user_id];
|
||||||
|
|
||||||
|
f.log("Profile Card [" + channel_id + "] " + user_id + " <" + JSON.stringify(data) + ">", this);
|
||||||
|
|
||||||
|
if ( ! data || ! el ) {
|
||||||
|
if ( t_el )
|
||||||
|
t_el.parentElement.removeChild(t_el);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = Date.now() - (f._ws_server_offset || 0),
|
||||||
|
age = data[0] ? Math.floor((now - data[0].getTime()) / 1000) : 0,
|
||||||
|
t_el = el.querySelector('.ffz-followed-since')
|
||||||
|
|
||||||
|
update_time = function() {
|
||||||
|
var now = Date.now() - (f._ws_server_offset || 0),
|
||||||
|
age = data && data[0] ? Math.floor((now - data[0].getTime()) / 1000) : undefined;
|
||||||
|
|
||||||
|
if ( age !== undefined ) {
|
||||||
|
t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
|
||||||
|
t_el.title = 'Follow' + (is_following ? 'ed by ' : 'er of ') + channel_id + ' since: <nobr>' + data[0].toLocaleString() + '</nobr>';
|
||||||
|
t_el.style.display = '';
|
||||||
|
} else
|
||||||
|
t_el.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( ! t_el ) {
|
||||||
|
t_el = createElement('div', 'overlay_info length html-tooltip ffz-followed-since');
|
||||||
|
el.appendChild(t_el);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_time();
|
||||||
|
|
||||||
|
}.observes('channelInfo')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -136,31 +136,6 @@ FFZ.settings_info.right_column_width = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.minimal_channel_title = {
|
|
||||||
type: "boolean",
|
|
||||||
value: false,
|
|
||||||
|
|
||||||
category: "Appearance",
|
|
||||||
no_mobile: true,
|
|
||||||
no_bttv: true,
|
|
||||||
|
|
||||||
name: "Minimal Channel Title",
|
|
||||||
help: "Hide the channel's name and current game when viewing a channel to maximize player size.",
|
|
||||||
|
|
||||||
on_update: function(val) {
|
|
||||||
if ( this.has_bttv )
|
|
||||||
return;
|
|
||||||
|
|
||||||
var Layout = utils.ember_lookup('service:layout');
|
|
||||||
if ( ! Layout )
|
|
||||||
return;
|
|
||||||
|
|
||||||
document.body.classList.toggle('ffz-minimal-channel-title', val);
|
|
||||||
Ember.propertyDidChange(Layout, 'windowHeight');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
// Initialization
|
// Initialization
|
||||||
// --------------------
|
// --------------------
|
||||||
|
@ -170,7 +145,6 @@ FFZ.prototype.setup_layout = function() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
document.body.classList.toggle("ffz-sidebar-swap", this.settings.swap_sidebars);
|
document.body.classList.toggle("ffz-sidebar-swap", this.settings.swap_sidebars);
|
||||||
document.body.classList.toggle('ffz-minimal-channel-title', this.settings.minimal_channel_title);
|
|
||||||
|
|
||||||
this.log("Creating layout style element.");
|
this.log("Creating layout style element.");
|
||||||
var s = this._layout_style = document.createElement('style');
|
var s = this._layout_style = document.createElement('style');
|
||||||
|
@ -232,8 +206,13 @@ FFZ.prototype.setup_layout = function() {
|
||||||
c = this.get('PLAYER_CONTROLS_HEIGHT'),
|
c = this.get('PLAYER_CONTROLS_HEIGHT'),
|
||||||
r = this.get('contentWidth'),
|
r = this.get('contentWidth'),
|
||||||
|
|
||||||
|
extra_height =
|
||||||
|
(f.settings.channel_bar_collapse ? 10 : 60) + 15 +
|
||||||
|
(f.settings.channel_title_top === 2 ? 20 : f.settings.channel_title_top > 0 ? 55 : 0) +
|
||||||
|
(f.settings.channel_title_top ? 70 : 80),
|
||||||
|
|
||||||
i = (9 * r / 16) + c,
|
i = (9 * r / 16) + c,
|
||||||
d = h - (f.settings.minimal_channel_title ? 75 : 120) - 60,
|
d = h - extra_height,
|
||||||
c = h - 94 - 185,
|
c = h - 94 - 185,
|
||||||
|
|
||||||
l = Math.floor(r),
|
l = Math.floor(r),
|
||||||
|
@ -268,6 +247,7 @@ FFZ.prototype.setup_layout = function() {
|
||||||
ffzUpdateCss: function() {
|
ffzUpdateCss: function() {
|
||||||
var window_height = this.get('windowHeight'),
|
var window_height = this.get('windowHeight'),
|
||||||
window_width = this.get('windowWidth'),
|
window_width = this.get('windowWidth'),
|
||||||
|
width = this.get('rightColumnWidth'),
|
||||||
out = 'body.ffz-small-player #player .dynamic-player {' +
|
out = 'body.ffz-small-player #player .dynamic-player {' +
|
||||||
'position: fixed;' +
|
'position: fixed;' +
|
||||||
'z-index: 9;' +
|
'z-index: 9;' +
|
||||||
|
@ -318,16 +298,29 @@ FFZ.prototype.setup_layout = function() {
|
||||||
'height:' + chat_height + 'px}' +
|
'height:' + chat_height + 'px}' +
|
||||||
'body[data-current-path^="user."] .app-main.theatre #left_col .warp,' +
|
'body[data-current-path^="user."] .app-main.theatre #left_col .warp,' +
|
||||||
'body[data-current-path^="user."] .app-main.theatre #left_col,' +
|
'body[data-current-path^="user."] .app-main.theatre #left_col,' +
|
||||||
|
'body[data-current-path^="user."] .app-main.theatre .cn-content #player,' +
|
||||||
'body[data-current-path^="user."] .app-main.theatre #main_col{' +
|
'body[data-current-path^="user."] .app-main.theatre #main_col{' +
|
||||||
'top:' + theatre_video_top + 'px;' +
|
'top:' + theatre_video_top + 'px;' +
|
||||||
'height:' + theatre_video_height + 'px}' +
|
'height:' + theatre_video_height + 'px !important}' +
|
||||||
'body[data-current-path^="user."] .app-main.theatre #right_col{' +
|
'body[data-current-path^="user."] .app-main.theatre #right_col{' +
|
||||||
'top:' + theatre_chat_top + 'px;' +
|
'top:' + theatre_chat_top + 'px;' +
|
||||||
'height:' + theatre_chat_height + 'px}';
|
'height:' + theatre_chat_height + 'px}' +
|
||||||
|
'.app-main.theatre .cn-content #player {' +
|
||||||
|
'right: 0 !important}' +
|
||||||
|
'body.ffz-minimal-channel-bar:not(.ffz-channel-bar-bottom) .cn-bar-fixed {' +
|
||||||
|
'top: ' + (video_top - 40) + 'px}' +
|
||||||
|
'body.ffz-minimal-channel-bar:not(.ffz-channel-bar-bottom) .cn-bar-fixed:hover,' +
|
||||||
|
'body:not(.ffz-channel-bar-bottom) .cn-bar-fixed {' +
|
||||||
|
'top: ' + video_top + 'px}' +
|
||||||
|
'.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar {' +
|
||||||
|
'bottom: ' + (chat_top - 40) + 'px}' +
|
||||||
|
'.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar:hover,' +
|
||||||
|
'.ffz-channel-bar-bottom .cn-bar {' +
|
||||||
|
'bottom: ' + chat_top + 'px}' +
|
||||||
|
'body:not(.ffz-sidebar-swap) .cn-bar-fixed { right: 0 !important }' +
|
||||||
|
'body.ffz-sidebar-swap .cn-bar-fixed { left: 0 !important }';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var width = this.get('rightColumnWidth');
|
|
||||||
|
|
||||||
out += 'top: 0; right: ' + width + 'px}' +
|
out += 'top: 0; right: ' + width + 'px}' +
|
||||||
'#main_col.expandRight #right_close{left: none !important}' +
|
'#main_col.expandRight #right_close{left: none !important}' +
|
||||||
'#right_col{width:' + width + 'px}' +
|
'#right_col{width:' + width + 'px}' +
|
||||||
|
@ -335,7 +328,16 @@ FFZ.prototype.setup_layout = function() {
|
||||||
'margin-right:' + width + 'px}' +
|
'margin-right:' + width + 'px}' +
|
||||||
'body.ffz-sidebar-swap .theatre #main_col:not(.expandRight),' +
|
'body.ffz-sidebar-swap .theatre #main_col:not(.expandRight),' +
|
||||||
'body.ffz-sidebar-swap #main_col:not(.expandRight){' +
|
'body.ffz-sidebar-swap #main_col:not(.expandRight){' +
|
||||||
'margin-left:' + width + 'px}';
|
'margin-left:' + width + 'px}' +
|
||||||
|
'body:not(.ffz-sidebar-swap) .app-main.theatre #main_col:not(.expandRight) .cn-content #player {' +
|
||||||
|
'right: ' + width + 'px !important}' +
|
||||||
|
'body.ffz-sidebar-swap .app-main.theatre #main_col:not(.expandRight) .cn-content #player {' +
|
||||||
|
'right: 0 !important;' +
|
||||||
|
'left:' + width + 'px !important}' +
|
||||||
|
'body:not(.ffz-sidebar-swap) #main_col:not(.expandRight) .cn-bar-fixed {' +
|
||||||
|
'right: ' + width + 'px}' +
|
||||||
|
'body.ffz-sidebar-swap #main_col:not(.expandRight) .cn-bar-fixed {' +
|
||||||
|
'left: ' + width + 'px !important}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
utils = require("../utils"),
|
utils = require("../utils"),
|
||||||
constants = require("../constants");
|
constants = require("../constants"),
|
||||||
|
|
||||||
|
BAN_SPLIT = /[/\.](?:ban ([^ ]+)|timeout ([^ ]+)(?: (\d+))?)(?: (.*))?$/;
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -211,29 +213,31 @@ FFZ.settings_info.banned_words = {
|
||||||
name: "Banned Words",
|
name: "Banned Words",
|
||||||
help: "Set a list of words that will be locally removed from chat messages.",
|
help: "Set a list of words that will be locally removed from chat messages.",
|
||||||
|
|
||||||
method: function() {
|
method: function(e, from_basic) {
|
||||||
var f = this,
|
var f = this,
|
||||||
old_val = this.settings.banned_words.join(", ");
|
old_val = this.settings.banned_words.join("\n"),
|
||||||
|
input = utils.createElement('textarea');
|
||||||
|
|
||||||
|
input.style.marginBottom = "20px";
|
||||||
|
|
||||||
utils.prompt(
|
utils.prompt(
|
||||||
"Banned Words",
|
"Banned Words",
|
||||||
"Please enter a comma-separated list of words that you would like to have removed from chat messages.",
|
"Please enter a list of words or phrases that you would like to have removed from chat messages. One item per line." + (from_basic ? "" : "<hr><strong>Advanced Stuff:</strong> If you know regex, you can use regular expressions to match too! Start a line with <code>regex:</code> to trigger that behavior.<br><div class=\"small\">(Note: Your expression is wrapped in a capture group and may be joined with other expressions within that group via <code>|</code>. All regular expressions are executed with the flags <code>ig</code>.)</div>"),
|
||||||
old_val,
|
old_val,
|
||||||
function(new_val) {
|
function(new_val) {
|
||||||
if ( new_val === null || new_val === undefined )
|
if ( new_val === null || new_val === undefined )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
new_val = new_val.trim().split(constants.SPLITTER);
|
var vals = new_val.trim().split(/\s*\n\s*/g),
|
||||||
var vals = [];
|
i = vals.length;
|
||||||
|
|
||||||
for(var i=0; i < new_val.length; i++)
|
while(i--)
|
||||||
new_val[i] && vals.push(new_val[i]);
|
if ( vals[i].length === 0 )
|
||||||
|
vals.splice(i, 1);
|
||||||
if ( vals.length == 1 && vals[0] == "disable" )
|
|
||||||
vals = [];
|
|
||||||
|
|
||||||
f.settings.set("banned_words", vals);
|
f.settings.set("banned_words", vals);
|
||||||
});
|
},
|
||||||
|
600, input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,30 +253,32 @@ FFZ.settings_info.keywords = {
|
||||||
name: "Highlight Keywords",
|
name: "Highlight Keywords",
|
||||||
help: "Set additional keywords that will be highlighted in chat.",
|
help: "Set additional keywords that will be highlighted in chat.",
|
||||||
|
|
||||||
method: function() {
|
method: function(e, from_basic) {
|
||||||
var f = this,
|
var f = this,
|
||||||
old_val = this.settings.keywords.join(", ");
|
old_val = this.settings.keywords.join("\n"),
|
||||||
|
input = utils.createElement('textarea');
|
||||||
|
|
||||||
|
input.style.marginBottom = "20px";
|
||||||
|
|
||||||
utils.prompt(
|
utils.prompt(
|
||||||
"Highlight Keywords",
|
"Highlight Keywords",
|
||||||
"Please enter a comma-separated list of words that you would like to be highlighted in chat.",
|
"Please enter a list of words or phrases that you would like to be highlighted in chat. One item per line." + (from_basic ? "" : "<hr><strong>Advanced Stuff:</strong> If you know regex, you can use regular expressions to match too! Start a line with <code>regex:</code> to trigger that behavior.<br><div class=\"small\">(Note: Your expression is wrapped in a capture group and may be joined with other expressions within that group via <code>|</code>. All regular expressions are executed with the flags <code>ig</code>.)</div>"),
|
||||||
old_val,
|
old_val,
|
||||||
function(new_val) {
|
function(new_val) {
|
||||||
if ( new_val === null || new_val === undefined )
|
if ( new_val === null || new_val === undefined )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Split them up.
|
// Split them up.
|
||||||
new_val = new_val.trim().split(constants.SPLITTER);
|
var vals = new_val.trim().split(/\s*\n\s*/g),
|
||||||
var vals = [];
|
i = vals.length;
|
||||||
|
|
||||||
for(var i=0; i < new_val.length; i++)
|
while(i--)
|
||||||
new_val[i] && vals.push(new_val[i]);
|
if ( vals[i].length === 0 )
|
||||||
|
vals.splice(i,1);
|
||||||
if ( vals.length == 1 && vals[0] == "disable" )
|
|
||||||
vals = [];
|
|
||||||
|
|
||||||
f.settings.set("keywords", vals);
|
f.settings.set("keywords", vals);
|
||||||
});
|
},
|
||||||
|
600, input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -720,7 +726,7 @@ FFZ.prototype.save_aliases = function() {
|
||||||
FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
var f = this,
|
var f = this,
|
||||||
Layout = utils.ember_lookup('service:layout'),
|
Layout = utils.ember_lookup('service:layout'),
|
||||||
Settings = utils.ember_lookup('controller:settings');
|
Settings = utils.ember_settings();
|
||||||
|
|
||||||
component.reopen({
|
component.reopen({
|
||||||
/*tokenizedMessage: function() {
|
/*tokenizedMessage: function() {
|
||||||
|
@ -784,22 +790,39 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
|
|
||||||
for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
|
for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
|
||||||
var pair = f.settings.mod_buttons[i],
|
var pair = f.settings.mod_buttons[i],
|
||||||
prefix = pair[0], btn = pair[1],
|
prefix = pair[0], btn = pair[1], had_label = pair[2], is_emoji = pair[3],
|
||||||
|
|
||||||
cmd, tip;
|
cmd, tip;
|
||||||
|
|
||||||
|
if ( is_emoji ) {
|
||||||
|
var setting = f.settings.parse_emoji,
|
||||||
|
token = f.emoji_data[is_emoji],
|
||||||
|
url = null;
|
||||||
|
if ( token ) {
|
||||||
|
if ( setting === 1 && token.tw )
|
||||||
|
url = token.tw_src;
|
||||||
|
else if ( setting === 2 && token.noto )
|
||||||
|
url = token.noto_src;
|
||||||
|
else if ( setting === 3 && token.one )
|
||||||
|
url = token.one_src;
|
||||||
|
|
||||||
|
if ( url )
|
||||||
|
prefix = '<img class="mod-icon-emoji" src="' + utils.quote_attr(url) + '">';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( btn === false ) {
|
if ( btn === false ) {
|
||||||
if ( deleted )
|
if ( deleted )
|
||||||
output += '<a class="mod-icon html-tooltip unban" title="Unban User" href="#">Unban</a>';
|
output += '<a class="mod-icon html-tooltip unban" title="Unban User" href="#">Unban</a>';
|
||||||
else
|
else
|
||||||
output += '<a class="mod-icon html-tooltip ban" title="Ban User" href="#">Ban</a>';
|
output += '<a class="mod-icon html-tooltip ban' + (had_label ? ' custom' : '') + '" title="Ban User" href="#">' + (had_label ? prefix : 'Ban') + '</a>';
|
||||||
|
|
||||||
} else if ( btn === 600 )
|
} else if ( btn === 600 )
|
||||||
output += '<a class="mod-icon html-tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>';
|
output += '<a class="mod-icon html-tooltip timeout' + (had_label ? ' custom' : '') + '" title="Timeout User (10m)" href="#">' + ( had_label ? prefix : 'Timeout') + '</a>';
|
||||||
|
|
||||||
else {
|
else {
|
||||||
if ( typeof btn === "string" ) {
|
if ( typeof btn === "string" ) {
|
||||||
cmd = btn.replace(/{user}/g, user).replace(/{id}/g, this.get('msgObject.tags.id')).replace(/ *<LINE> */, "\n");
|
cmd = utils.replace_cmd_variables(btn, {name: user}, room && room.room, this.get('msgObject')).replace(/\s*<LINE>\s*/g, '\n');
|
||||||
tip = "Custom Command" + (cmd.indexOf("\n") !== -1 ? 's' : '') + '<br>' + utils.quote_san(cmd).replace('\n','<br>');
|
tip = "Custom Command" + (cmd.indexOf("\n") !== -1 ? 's' : '') + '<br>' + utils.quote_san(cmd).replace('\n','<br>');
|
||||||
} else {
|
} else {
|
||||||
cmd = "/timeout " + user + " " + btn;
|
cmd = "/timeout " + user + " " + btn;
|
||||||
|
@ -819,7 +842,7 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
|
|
||||||
raw_color = this.get(is_recipient ? 'msgObject.toColor' : 'msgObject.color'),
|
raw_color = this.get(is_recipient ? 'msgObject.toColor' : 'msgObject.color'),
|
||||||
|
|
||||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode'))),
|
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('darkMode'))),
|
||||||
is_replay = this.get('ffz_is_replay'),
|
is_replay = this.get('ffz_is_replay'),
|
||||||
|
|
||||||
colors = raw_color && f._handle_color(raw_color),
|
colors = raw_color && f._handle_color(raw_color),
|
||||||
|
@ -845,7 +868,9 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp
|
// Timestamp
|
||||||
output += '<span class="timestamp">' + this.get('timestamp') + '</span> ';
|
var timestamp = this.get('timestamp');
|
||||||
|
if ( timestamp )
|
||||||
|
output += '<span class="timestamp">' + timestamp + '</span> ';
|
||||||
|
|
||||||
// Moderator Actions
|
// Moderator Actions
|
||||||
output += this.buildModIconsHTML();
|
output += this.buildModIconsHTML();
|
||||||
|
@ -877,7 +902,7 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
var raw_color = this.get('msgObject.color'),
|
var raw_color = this.get('msgObject.color'),
|
||||||
colors = raw_color && f._handle_color(raw_color),
|
colors = raw_color && f._handle_color(raw_color),
|
||||||
is_replay = this.get('ffz_is_replay'),
|
is_replay = this.get('ffz_is_replay'),
|
||||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode')));
|
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('darkMode')));
|
||||||
|
|
||||||
if ( raw_color )
|
if ( raw_color )
|
||||||
output = '<span class="message has-color' + (is_replay ? ' replay-color' : '') + '" style="color:' + (is_dark ? colors[1] : colors[0]) + '" data-color="' + raw_color + '">';
|
output = '<span class="message has-color' + (is_replay ? ' replay-color' : '') + '" style="color:' + (is_dark ? colors[1] : colors[0]) + '" data-color="' + raw_color + '">';
|
||||||
|
@ -886,7 +911,11 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
} else
|
} else
|
||||||
output = '<span class="message">';
|
output = '<span class="message">';
|
||||||
|
|
||||||
output += f.render_tokens(this.get('ffzTokenizedMessage'), true, is_whisper && f.settings.filter_whispered_links && this.get("ffzUserLevel") < 4, this.get('isBitsEnabled'));
|
var body = f.render_tokens(this.get('ffzTokenizedMessage'), true, is_whisper && f.settings.filter_whispered_links && this.get("ffzUserLevel") < 4, this.get('isBitsEnabled'));
|
||||||
|
if ( this.get('msgObject.ffz_line_returns') )
|
||||||
|
body = body.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
|
output += body;
|
||||||
|
|
||||||
var old_messages = this.get('msgObject.ffz_old_messages');
|
var old_messages = this.get('msgObject.ffz_old_messages');
|
||||||
if ( old_messages && old_messages.length )
|
if ( old_messages && old_messages.length )
|
||||||
|
@ -913,9 +942,9 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||||
return ! this.get('hasSystemMsg') || this.get('hasMessageBody');
|
return ! this.get('hasSystemMsg') || this.get('hasMessageBody');
|
||||||
}.property('hasSystemMsg', 'hasMessageBody'),
|
}.property('hasSystemMsg', 'hasMessageBody'),
|
||||||
|
|
||||||
shouldRenderMessageBody: function() {
|
//shouldRenderMessageBody: function() {
|
||||||
return false;
|
// return false;
|
||||||
}.property('hasSystemMsg', 'hasMessageBody'),
|
//}.property('hasSystemMsg', 'hasMessageBody'),
|
||||||
|
|
||||||
ffzWasDeleted: function() {
|
ffzWasDeleted: function() {
|
||||||
return f.settings.prevent_clear && this.get("msgObject.ffz_deleted")
|
return f.settings.prevent_clear && this.get("msgObject.ffz_deleted")
|
||||||
|
@ -949,6 +978,135 @@ FFZ.prototype._modify_chat_subline = function(component) {
|
||||||
|
|
||||||
//didUpdate: function() { this.ffzRender(); },
|
//didUpdate: function() { this.ffzRender(); },
|
||||||
|
|
||||||
|
ffzBuildModMenu: function(el) {
|
||||||
|
var t = this,
|
||||||
|
setting = f.settings.mod_button_context,
|
||||||
|
cl = el.classList,
|
||||||
|
from = this.get("msgObject.from"),
|
||||||
|
cmd = el.getAttribute('data-cmd'),
|
||||||
|
trail = '';
|
||||||
|
|
||||||
|
if ( ! cmd && cl.contains('ban') )
|
||||||
|
cmd = "/ban " + from;
|
||||||
|
else if ( ! cmd && cl.contains('timeout') )
|
||||||
|
cmd = "/timeout " + from + " 600";
|
||||||
|
else if ( ! cmd || cl.contains('unban') )
|
||||||
|
return; // We can't send mod reasons for unbans and we need a command.
|
||||||
|
else {
|
||||||
|
var lines = cmd.split("\n"),
|
||||||
|
first_line = lines.shift(),
|
||||||
|
trail = lines.length ? "\n" + lines.join("\n") : "",
|
||||||
|
match = BAN_SPLIT.exec(first_line);
|
||||||
|
|
||||||
|
// If the line didn't match this, it's invalid.
|
||||||
|
if ( ! match )
|
||||||
|
return;
|
||||||
|
|
||||||
|
cmd = match[1] ? "/ban " + match[1] : "/timeout " + match[2] + " " + (match[3] || "600");
|
||||||
|
if ( match[4] )
|
||||||
|
trail = match[4] + trail;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bl = utils.createElement('ul', 'balloon__list'),
|
||||||
|
balloon = utils.createElement('div', 'balloon balloon--dropmenu ffz-mod-balloon', bl),
|
||||||
|
bc = utils.createElement('div', 'balloon-wrapper', balloon),
|
||||||
|
has_items = false,
|
||||||
|
|
||||||
|
is_ban = cmd.substr(1, 4) === 'ban ',
|
||||||
|
title = utils.createElement('li', 'ffz-title');
|
||||||
|
|
||||||
|
title.textContent = (is_ban ? 'Ban ' : 'Timeout ') + from + ' for...';
|
||||||
|
bl.appendChild(title);
|
||||||
|
bl.appendChild(utils.createElement('li', 'balloon__stroke'));
|
||||||
|
|
||||||
|
var btn_click = function(reason, e) {
|
||||||
|
if ( e.button !== 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var room_id = t.get('msgObject.room'),
|
||||||
|
room = room_id && f.rooms[room_id] && f.rooms[room_id].room;
|
||||||
|
|
||||||
|
if ( room ) {
|
||||||
|
cmd = cmd + ' ' + reason + (trail ? (trail[0] === '\n' ? '' : ' ') + trail : '');
|
||||||
|
var lines = cmd.split("\n");
|
||||||
|
for(var i=0; i < lines.length; i++)
|
||||||
|
room.send(lines[i], true);
|
||||||
|
|
||||||
|
if ( cl.contains('is-timeout') )
|
||||||
|
room.clearMessages(from, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.close_popup();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( setting & 1 )
|
||||||
|
for(var i=0; i < f.settings.mod_card_reasons.length; i++) {
|
||||||
|
var btn = utils.createElement('div', 'balloon__link ellipsis'),
|
||||||
|
line = utils.createElement('li', '', btn),
|
||||||
|
reason = f.settings.mod_card_reasons[i];
|
||||||
|
|
||||||
|
btn.textContent = btn.title = reason;
|
||||||
|
btn.addEventListener('click', btn_click.bind(btn, reason));
|
||||||
|
bl.appendChild(line);
|
||||||
|
has_items = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( setting & 2 ) {
|
||||||
|
var room_id = t.get('msgObject.room'),
|
||||||
|
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
|
||||||
|
rules = room && room.get('roomProperties.chat_rules');
|
||||||
|
|
||||||
|
if ( rules && rules.length ) {
|
||||||
|
if ( has_items )
|
||||||
|
bl.appendChild(utils.createElement('li', 'balloon__stroke'));
|
||||||
|
|
||||||
|
for(var i=0; i < rules.length; i++) {
|
||||||
|
var btn = utils.createElement('div', 'balloon__link ellipsis'),
|
||||||
|
line = utils.createElement('li', '', btn),
|
||||||
|
reason = rules[i];
|
||||||
|
|
||||||
|
btn.textContent = btn.title = reason;
|
||||||
|
btn.addEventListener('click', btn_click.bind(btn, reason));
|
||||||
|
bl.appendChild(line);
|
||||||
|
has_items = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! has_items )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var rect = el.getBoundingClientRect(),
|
||||||
|
is_bottom = rect.top > (window.innerHeight / 2),
|
||||||
|
position = [rect.left, (is_bottom ? rect.top : rect.bottom)];
|
||||||
|
|
||||||
|
balloon.classList.add('balloon--' + (is_bottom ? 'up' : 'down'));
|
||||||
|
|
||||||
|
f.show_popup(bc, position, utils.find_parent(this.get('element'), 'chat-messages'));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
contextMenu: function(e) {
|
||||||
|
if ( ! e.target )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var cl = e.target.classList,
|
||||||
|
from = this.get("msgObject.from"),
|
||||||
|
abort = false;
|
||||||
|
|
||||||
|
// We only want to show a context menu for mod icons right now.
|
||||||
|
if ( cl.contains('mod-icon') )
|
||||||
|
abort |= this.ffzBuildModMenu(e.target);
|
||||||
|
|
||||||
|
if ( abort ) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
if ( ! e.target )
|
if ( ! e.target )
|
||||||
return;
|
return;
|
||||||
|
@ -970,7 +1128,16 @@ FFZ.prototype._modify_chat_subline = function(component) {
|
||||||
jQuery(e.target).trigger('mouseout');
|
jQuery(e.target).trigger('mouseout');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if ( cl.contains('custom') ) {
|
if ( cl.contains('ban') )
|
||||||
|
this.sendAction("banUser", {user:from});
|
||||||
|
|
||||||
|
else if ( cl.contains('unban') )
|
||||||
|
this.sendAction("unbanUser", {user:from});
|
||||||
|
|
||||||
|
else if ( cl.contains('timeout') )
|
||||||
|
this.sendAction("timeoutUser", {user:from});
|
||||||
|
|
||||||
|
else if ( cl.contains('custom') ) {
|
||||||
var room_id = this.get('msgObject.room'),
|
var room_id = this.get('msgObject.room'),
|
||||||
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
|
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
|
||||||
cmd = e.target.getAttribute('data-cmd');
|
cmd = e.target.getAttribute('data-cmd');
|
||||||
|
@ -985,14 +1152,7 @@ FFZ.prototype._modify_chat_subline = function(component) {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if ( cl.contains('ban') )
|
}
|
||||||
this.sendAction("banUser", {user:from});
|
|
||||||
|
|
||||||
else if ( cl.contains('unban') )
|
|
||||||
this.sendAction("unbanUser", {user:from});
|
|
||||||
|
|
||||||
else if ( cl.contains('timeout') )
|
|
||||||
this.sendAction("timeoutUser", {user:from});
|
|
||||||
|
|
||||||
} else if ( cl.contains('badge') ) {
|
} else if ( cl.contains('badge') ) {
|
||||||
if ( cl.contains('click_url') )
|
if ( cl.contains('click_url') )
|
||||||
|
|
|
@ -6,7 +6,6 @@ var FFZ = window.FrankerFaceZ,
|
||||||
|
|
||||||
TO_REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/,
|
TO_REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/,
|
||||||
BAN_REG = /^\/b(?:an)? +([^ ]+)(?: +(.+))?$/,
|
BAN_REG = /^\/b(?:an)? +([^ ]+)(?: +(.+))?$/,
|
||||||
USER_REG = /\{user\}/g,
|
|
||||||
|
|
||||||
keycodes = {
|
keycodes = {
|
||||||
ESC: 27,
|
ESC: 27,
|
||||||
|
@ -102,8 +101,7 @@ FFZ.settings_info.chat_mod_icon_visibility = {
|
||||||
},
|
},
|
||||||
|
|
||||||
value: function() {
|
value: function() {
|
||||||
var settings = utils.ember_lookup('controller:settings');
|
return this.settings.get_twitch("showModIcons") ? 1 : 0;
|
||||||
return (settings && settings.get('settings.showModIcons')) ? 1 : 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
process_value: function(val) {
|
process_value: function(val) {
|
||||||
|
@ -119,9 +117,9 @@ FFZ.settings_info.chat_mod_icon_visibility = {
|
||||||
help: "Choose when you should see in-line moderation icons in chat.",
|
help: "Choose when you should see in-line moderation icons in chat.",
|
||||||
|
|
||||||
on_update: function(val) {
|
on_update: function(val) {
|
||||||
var settings = utils.ember_lookup('controller:settings');
|
var settings = utils.ember_settings();
|
||||||
if ( settings )
|
if ( settings )
|
||||||
settings.set('settings.showModIcons', val === 1);
|
settings.set('showModIcons', val === 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +258,33 @@ FFZ.settings_info.mod_card_history = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.mod_button_context = {
|
||||||
|
type: "select",
|
||||||
|
options: {
|
||||||
|
0: "Disabled",
|
||||||
|
1: "Show Ban Reasons Only",
|
||||||
|
2: "Show Chat Rules Only",
|
||||||
|
3: "Ban Reasons + Chat Rules"
|
||||||
|
},
|
||||||
|
|
||||||
|
value: 3,
|
||||||
|
process_value: function(val) {
|
||||||
|
if ( typeof val === "string" ) {
|
||||||
|
val = parseInt(val);
|
||||||
|
if ( isNaN(val) || ! isFinite(val) )
|
||||||
|
val = 3;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
|
||||||
|
category: "Chat Moderation",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Mod Icon Context Menus",
|
||||||
|
help: "Choose the available options when right-clicking an in-line moderation icon."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.mod_card_reasons = {
|
FFZ.settings_info.mod_card_reasons = {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: [
|
value: [
|
||||||
|
@ -274,8 +299,8 @@ FFZ.settings_info.mod_card_reasons = {
|
||||||
category: "Chat Moderation",
|
category: "Chat Moderation",
|
||||||
no_bttv: true,
|
no_bttv: true,
|
||||||
|
|
||||||
name: "Moderation Card Ban Reasons",
|
name: "Ban / Timeout Reasons",
|
||||||
help: "Change the available options in the chat moderation card ban reasons list.",
|
help: "Change the available options in the chat ban reasons list shown in moderation cards and when right-clicking an in-line ban or timeout button.",
|
||||||
|
|
||||||
method: function() {
|
method: function() {
|
||||||
var f = this,
|
var f = this,
|
||||||
|
@ -286,7 +311,7 @@ FFZ.settings_info.mod_card_reasons = {
|
||||||
|
|
||||||
utils.prompt(
|
utils.prompt(
|
||||||
"Moderation Card Ban Reasons",
|
"Moderation Card Ban Reasons",
|
||||||
"Please enter a list of ban reasons to select from in chat moderation cards. One item per line.",
|
"Please enter a list of ban reasons to select from. One item per line.",
|
||||||
old_val,
|
old_val,
|
||||||
function(new_val) {
|
function(new_val) {
|
||||||
if ( new_val === null || new_val === undefined )
|
if ( new_val === null || new_val === undefined )
|
||||||
|
@ -323,127 +348,124 @@ FFZ.settings_info.mod_buttons = {
|
||||||
|
|
||||||
method: function() {
|
method: function() {
|
||||||
var f = this,
|
var f = this,
|
||||||
old_val = "";
|
old_val = "",
|
||||||
|
input = utils.createElement('textarea');
|
||||||
|
|
||||||
|
input.style.marginBottom = '20px';
|
||||||
|
input.placeholder = '/ban\n600';
|
||||||
|
|
||||||
for(var i=0; i < this.settings.mod_buttons.length; i++) {
|
for(var i=0; i < this.settings.mod_buttons.length; i++) {
|
||||||
var pair = this.settings.mod_buttons[i],
|
var pair = this.settings.mod_buttons[i],
|
||||||
prefix = pair[0], cmd = pair[1], had_prefix = pair[2];
|
prefix = pair[0], cmd = pair[1], had_prefix = pair[2];
|
||||||
|
|
||||||
if ( cmd === false )
|
if ( cmd === false )
|
||||||
cmd = "<BAN>";
|
cmd = "/ban";
|
||||||
|
else if ( cmd === 600 )
|
||||||
|
cmd = "/timeout";
|
||||||
else if ( typeof cmd !== "string" )
|
else if ( typeof cmd !== "string" )
|
||||||
cmd = '' + cmd;
|
cmd = '' + cmd;
|
||||||
|
|
||||||
if ( ! had_prefix )
|
prefix = had_prefix ? 'name:' + prefix + '=' : '';
|
||||||
prefix = '';
|
old_val += (old_val.length ? '\n' : '') + prefix + cmd;
|
||||||
else
|
|
||||||
prefix += '=';
|
|
||||||
|
|
||||||
if ( cmd.substr(cmd.length - 7) === ' {user}' )
|
|
||||||
cmd = cmd.substr(0, cmd.length - 7);
|
|
||||||
|
|
||||||
if ( cmd.indexOf(' ') !== -1 )
|
|
||||||
old_val += ' ' + prefix + '"' + cmd + '"';
|
|
||||||
else
|
|
||||||
old_val += ' ' + prefix + cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
utils.prompt(
|
utils.prompt(
|
||||||
"Custom In-Line Moderation Icons",
|
"Custom In-Line Moderation Icons",
|
||||||
"Please enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. " +
|
"Please enter a list of commands to be displayed as moderation buttons within chat lines. " +
|
||||||
"To include spaces in a command, surround the command with double quotes (\"). Use <code>{user}</code> to insert the user's name " +
|
"One item per line. As a shortcut for specific duration timeouts, you can enter the number of seconds by itself. " +
|
||||||
"into the command, otherwise it will be appended to the end. Use <code>{id}</code> to insert the unique message ID into the command.</p>" +
|
" To send multiple commands, separate them with <code><LINE></code>. " +
|
||||||
"<p><b>Example:</b> <code>!permit \"!reg add {user}\" \"/timeout {user} 1 {id}\"</code></p><p>To " +
|
"Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
|
||||||
"send multiple commands, separate them with <code><LINE></code>.</p><p>Numeric values will become timeout buttons for " +
|
"in a line, <code>{user}</code> will be added to the end of the first command.<hr>" +
|
||||||
"that number of seconds. The text <code><BAN></code> is a special value that will act like the normal Ban button in chat.</p><p>" +
|
|
||||||
"To assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.</p><p>" +
|
"To set a custom label for the button, start your line with <code>name:</code> followed by the " +
|
||||||
"<b>Example:</b> <code>A=\"!reg add\"</code></p><p><b>Default:</b> <code><BAN> 600</code>",
|
"name of the button. End the name with an equals sign. Only the first character will be displayed.<br>" +
|
||||||
old_val.substr(1),
|
"<strong>Example:</strong> <code>name:B=/ban {user}</code><hr>" +
|
||||||
|
|
||||||
|
"<strong>Allowed Variables</strong><br><table><tbody>" +
|
||||||
|
"<tr><td><code>{user}</code></td><td>target user's name</td>" +
|
||||||
|
"<td><code>{user_name}</code></td><td>target user's name</td></tr>" +
|
||||||
|
"<tr><td><code>{user_display_name}</code></td><td>target user's display name</td>" +
|
||||||
|
"<td><code>{user_id}</code></td><td>target user's numeric ID</td></tr>" +
|
||||||
|
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
|
||||||
|
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
|
||||||
|
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
|
||||||
|
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
|
||||||
|
"<tr><td><code>{id}</code></td><td>message's UUID</td></tr>" +
|
||||||
|
"</tbody></table>",
|
||||||
|
|
||||||
|
old_val,
|
||||||
function(new_val) {
|
function(new_val) {
|
||||||
if ( new_val === null || new_val === undefined )
|
if ( new_val === null || new_val === undefined )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var vals = [], prefix = '';
|
var vals = new_val.trim().split(/\s*\n\s*/g),
|
||||||
new_val = new_val.trim();
|
output = [];
|
||||||
|
|
||||||
while(new_val) {
|
|
||||||
if ( new_val.charAt(1) === '=' ) {
|
|
||||||
prefix = new_val.charAt(0);
|
|
||||||
new_val = new_val.substr(2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( new_val.charAt(0) === '"' ) {
|
|
||||||
var end = new_val.indexOf('"', 1);
|
|
||||||
if ( end === -1 )
|
|
||||||
end = new_val.length;
|
|
||||||
|
|
||||||
var segment = new_val.substr(1, end - 1);
|
|
||||||
if ( segment ) {
|
|
||||||
vals.push([prefix, segment]);
|
|
||||||
prefix = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
new_val = new_val.substr(end + 1);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var ind = new_val.indexOf(' ');
|
|
||||||
if ( ind === -1 ) {
|
|
||||||
if ( new_val ) {
|
|
||||||
vals.push([prefix, new_val]);
|
|
||||||
prefix = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
new_val = '';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var segment = new_val.substr(0, ind);
|
|
||||||
if ( segment ) {
|
|
||||||
vals.push([prefix, segment]);
|
|
||||||
prefix = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
new_val = new_val.substr(ind + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var final = [];
|
|
||||||
for(var i=0; i < vals.length; i++) {
|
for(var i=0; i < vals.length; i++) {
|
||||||
var had_prefix = false, prefix = vals[i][0], val = vals[i][1];
|
var cmd = vals[i],
|
||||||
if ( val === "<BAN>" )
|
prefix,
|
||||||
val = false;
|
is_emoji = false,
|
||||||
|
name_match = /^name:([^=]+)=/.exec(cmd);
|
||||||
|
|
||||||
var num = parseInt(val);
|
if ( ! cmd || ! cmd.length )
|
||||||
if ( num > 0 && ! Number.isNaN(num) )
|
continue;
|
||||||
val = num;
|
|
||||||
|
|
||||||
if ( ! prefix ) {
|
if ( name_match ) {
|
||||||
var tmp;
|
label = name_match[1];
|
||||||
if ( typeof val === "string" )
|
|
||||||
tmp = /\w/.exec(val);
|
|
||||||
else
|
|
||||||
tmp = utils.duration_string(val);
|
|
||||||
|
|
||||||
prefix = tmp && tmp.length ? tmp[0].toUpperCase() : "C";
|
if ( window.punycode && punycode.ucs2 )
|
||||||
|
label = punycode.ucs2.encode([punycode.ucs2.decode(label)[0]]);
|
||||||
|
|
||||||
|
// Check for an emoji
|
||||||
|
var tokens = f.tokenize_emoji(label);
|
||||||
|
if ( tokens && tokens[0] && tokens[0].ffzEmoji )
|
||||||
|
is_emoji = tokens[0].ffzEmoji;
|
||||||
|
|
||||||
|
cmd = cmd.substr(name_match[0].length).trim();
|
||||||
} else
|
} else
|
||||||
had_prefix = true;
|
label = undefined;
|
||||||
|
|
||||||
if ( typeof val === "string" ) {
|
// Check for a plain ban.
|
||||||
// Split it up for this step.
|
if ( /^\/b(?:an)?(?:\s+{user(?:_name)?})?\s*$/.test(cmd) )
|
||||||
var lines = val.split(/ *<LINE> */);
|
cmd = false;
|
||||||
for(var x=0; x < lines.length; x++) {
|
|
||||||
if ( lines[x].indexOf('{user}') === -1 )
|
// Numeric Timeout
|
||||||
lines[x] += ' {user}';
|
else if ( /^\d+$/.test(cmd) )
|
||||||
}
|
cmd = parseInt(cmd);
|
||||||
val = lines.join("<LINE>");
|
|
||||||
|
// Command Timeout
|
||||||
|
else if ( /^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/.test(cmd) ) {
|
||||||
|
cmd = parseInt(/^\/t(?:imeout)?(?:\s+{user(?:_name)?}(?:\s+(\d+))?)?\s*$/.exec(cmd)[1]);
|
||||||
|
if ( isNaN(cmd) || ! isFinite(cmd) )
|
||||||
|
cmd = 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
final.push([prefix, val, had_prefix]);
|
|
||||||
|
// Okay. Do we still need a prefix?
|
||||||
|
if ( label === undefined ) {
|
||||||
|
var tmp;
|
||||||
|
if ( typeof cmd === "string" )
|
||||||
|
tmp = /\w/.exec(cmd);
|
||||||
|
else
|
||||||
|
tmp = utils.duration_string(cmd);
|
||||||
|
|
||||||
|
label = tmp && tmp.length ? tmp[0].toUpperCase() : 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
f.settings.set('mod_buttons', final);
|
// Add {user} to the first command if it's a custom command and missing.
|
||||||
|
if ( typeof cmd === "string" ) {
|
||||||
|
utils.CMD_VAR_REGEX.lastIndex = 0;
|
||||||
|
if ( ! utils.CMD_VAR_REGEX.test(cmd) ) {
|
||||||
|
var lines = cmd.split(/\s*<LINE>\s*/g);
|
||||||
|
lines[0] += ' {user}';
|
||||||
|
cmd = lines.join("<LINE>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push([label, cmd, name_match != null, is_emoji]);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.settings.set('mod_buttons', output);
|
||||||
|
|
||||||
// Update existing chat lines.
|
// Update existing chat lines.
|
||||||
var CL = utils.ember_resolve('component:chat/chat-line'),
|
var CL = utils.ember_resolve('component:chat/chat-line'),
|
||||||
|
@ -455,7 +477,7 @@ FFZ.settings_info.mod_buttons = {
|
||||||
view.$('.mod-icons').replaceWith(view.buildModIconsHTML());
|
view.$('.mod-icons').replaceWith(view.buildModIconsHTML());
|
||||||
}
|
}
|
||||||
|
|
||||||
}, 600);
|
}, 600, input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -472,60 +494,74 @@ FFZ.settings_info.mod_card_buttons = {
|
||||||
|
|
||||||
method: function() {
|
method: function() {
|
||||||
var f = this,
|
var f = this,
|
||||||
old_val = "";
|
old_val = "",
|
||||||
|
input = utils.createElement('textarea');
|
||||||
|
|
||||||
|
input.style.marginBottom = '20px';
|
||||||
|
|
||||||
for(var i=0; i < this.settings.mod_card_buttons.length; i++) {
|
for(var i=0; i < this.settings.mod_card_buttons.length; i++) {
|
||||||
var cmd = this.settings.mod_card_buttons[i];
|
var label, cmd, had_label, pair = this.settings.mod_card_buttons[i];
|
||||||
if ( cmd.indexOf(' ') !== -1 )
|
if ( Array.isArray(pair) ) {
|
||||||
old_val += ' "' + cmd + '"';
|
label = pair[0];
|
||||||
else
|
cmd = pair[1];
|
||||||
old_val += ' ' + cmd;
|
had_label = pair[2];
|
||||||
|
} else {
|
||||||
|
cmd = pair;
|
||||||
|
had_label = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
label = had_label ? 'name:' + label + '=' : '';
|
||||||
|
old_val += (old_val.length ? '\n' : '') + label + cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.prompt(
|
utils.prompt(
|
||||||
"Moderation Card Additional Buttons",
|
"Moderation Card Additional Buttons",
|
||||||
"Please enter a list of additional commands to display buttons for on moderation cards. Commands are separated by spaces. " +
|
"Please enter a list of additional commands to display buttons for on moderation cards. " +
|
||||||
"To include spaces in a command, surround the command with double quotes (\"). Use <code>{user}</code> to insert the " +
|
"One item per line. To send multiple commands, separate them with <code><LINE></code>. " +
|
||||||
"user's name into the command, otherwise it will be appended to the end.</p><p><b>Example:</b> !permit \"!reg add {user}\"",
|
"Variables, such as the target user's name, can be inserted into your commands. If no variables are detected " +
|
||||||
old_val.substr(1),
|
"in a line, <code>{user}</code> will be added to the end of the first command.<hr>" +
|
||||||
|
|
||||||
|
"To set a custom label for the button, start your line with <code>name:</code> followed by the name of the button. " +
|
||||||
|
"End the name with an equals sign.<br>" +
|
||||||
|
"<strong>Example:</strong> <code>name:Boop=/timeout {user} 15 Boop!</code><hr>" +
|
||||||
|
|
||||||
|
"<strong>Allowed Variables</strong><br><table><tbody>" +
|
||||||
|
"<tr><td><code>{user}</code></td><td>target user's name</td>" +
|
||||||
|
"<td><code>{user_name}</code></td><td>target user's name</td></tr>" +
|
||||||
|
"<tr><td><code>{user_display_name}</code></td><td>target user's display name</td>" +
|
||||||
|
"<td><code>{user_id}</code></td><td>target user's numeric ID</td></tr>" +
|
||||||
|
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
|
||||||
|
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
|
||||||
|
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
|
||||||
|
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
|
||||||
|
"</tbody></table>",
|
||||||
|
old_val,
|
||||||
function(new_val) {
|
function(new_val) {
|
||||||
if ( new_val === null || new_val === undefined )
|
if ( new_val === null || new_val === undefined )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var vals = [];
|
var vals = new_val.trim().split(/\s*\n\s*/g),
|
||||||
new_val = new_val.trim();
|
output = [];
|
||||||
|
|
||||||
while(new_val) {
|
for(var i=0; i < vals.length; i++) {
|
||||||
if ( new_val.charAt(0) === '"' ) {
|
var cmd = vals[i],
|
||||||
var end = new_val.indexOf('"', 1);
|
label,
|
||||||
if ( end === -1 )
|
name_match = /^name:([^=]+)=/.exec(cmd);
|
||||||
end = new_val.length;
|
|
||||||
|
|
||||||
var segment = new_val.substr(1, end - 1);
|
if ( ! cmd || ! cmd.length )
|
||||||
if ( segment )
|
continue;
|
||||||
vals.push(segment);
|
|
||||||
|
|
||||||
new_val = new_val.substr(end + 1);
|
if ( name_match ) {
|
||||||
|
label = name_match[1];
|
||||||
|
cmd = cmd.substr(name_match[0].length);
|
||||||
|
} else
|
||||||
|
label = cmd.split(' ', 1)[0]
|
||||||
|
|
||||||
} else {
|
output.push([label, cmd, name_match != null]);
|
||||||
var ind = new_val.indexOf(' ');
|
|
||||||
if ( ind === -1 ) {
|
|
||||||
if ( new_val )
|
|
||||||
vals.push(new_val);
|
|
||||||
|
|
||||||
new_val = '';
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var segment = new_val.substr(0, ind);
|
|
||||||
if ( segment )
|
|
||||||
vals.push(segment);
|
|
||||||
|
|
||||||
new_val = new_val.substr(ind + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.settings.set("mod_card_buttons", vals);
|
f.settings.set("mod_card_buttons", output);
|
||||||
}, 600);
|
}, 600, input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -586,11 +622,11 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
|
|
||||||
this.log("Listening to the Settings controller to catch mod icon state changes.");
|
this.log("Listening to the Settings controller to catch mod icon state changes.");
|
||||||
var f = this,
|
var f = this,
|
||||||
Settings = utils.ember_lookup('controller:settings');
|
Settings = utils.ember_settings();
|
||||||
|
|
||||||
if ( Settings )
|
if ( Settings )
|
||||||
Settings.addObserver('settings.showModIcons', function() {
|
Settings.addObserver('showModIcons', function() {
|
||||||
if ( Settings.get('settings.showModIcons') )
|
if ( Settings.get('showModIcons') )
|
||||||
f.settings.set('chat_mod_icon_visibility', 1);
|
f.settings.set('chat_mod_icon_visibility', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -684,7 +720,8 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
||||||
|
|
||||||
chat = utils.ember_lookup('controller:chat'),
|
chat = utils.ember_lookup('controller:chat'),
|
||||||
user = f.get_user(),
|
user = f.get_user(),
|
||||||
room_id = chat && chat.get('currentRoom.id'),
|
room = chat && chat.get('currentRoom'),
|
||||||
|
room_id = room && room.get('id'),
|
||||||
is_broadcaster = user && room_id === user.login,
|
is_broadcaster = user && room_id === user.login,
|
||||||
|
|
||||||
user_id = controller.get('cardInfo.user.id'),
|
user_id = controller.get('cardInfo.user.id'),
|
||||||
|
@ -747,13 +784,12 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
||||||
if ( is_mod && f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
|
if ( is_mod && f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
|
||||||
line = utils.createElement('div', 'extra-interface interface clearfix');
|
line = utils.createElement('div', 'extra-interface interface clearfix');
|
||||||
|
|
||||||
var cmds = {},
|
var add_btn_click = function(cmd) {
|
||||||
add_btn_click = function(cmd) {
|
var user = controller.get('cardInfo.user'),
|
||||||
var user_id = controller.get('cardInfo.user.id'),
|
chat_controller = utils.ember_lookup('controller:chat'),
|
||||||
cont = utils.ember_lookup('controller:chat'),
|
room = chat_controller && chat_controller.get('currentRoom'),
|
||||||
room = cont && cont.get('currentRoom'),
|
|
||||||
|
|
||||||
cm = cmd.replace(USER_REG, user_id),
|
cm = utils.replace_cmd_variables(cmd, user, room),
|
||||||
reason = ban_reason();
|
reason = ban_reason();
|
||||||
|
|
||||||
if ( reason ) {
|
if ( reason ) {
|
||||||
|
@ -775,36 +811,49 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
||||||
room && room.send(cm, true);
|
room && room.send(cm, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
add_btn_make = function(cmd) {
|
add_btn_make = function(label, cmd) {
|
||||||
var btn = utils.createElement('button', 'button ffz-no-bg'),
|
var btn = utils.createElement('button', 'button ffz-no-bg', utils.sanitize(label));
|
||||||
segment = cmd.split(' ', 1)[0],
|
|
||||||
title = cmds[segment] > 1 ? cmd.split(' ', cmds[segment]) : [segment];
|
|
||||||
|
|
||||||
if ( /^[!~./]/.test(title[0]) )
|
jQuery(btn).tipsy({
|
||||||
title[0] = title[0].substr(1);
|
html: true,
|
||||||
|
gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n'),
|
||||||
|
title: function() {
|
||||||
|
var user = controller.get('cardInfo.user'),
|
||||||
|
chat_controller = utils.ember_lookup('controller:chat'),
|
||||||
|
room = chat_controller && chat_controller.get('currentRoom');
|
||||||
|
|
||||||
title = _.map(title, function(s){ return s.capitalize() }).join(' ');
|
title = utils.replace_cmd_variables(cmd, user, room);
|
||||||
|
|
||||||
btn.innerHTML = utils.sanitize(title);
|
title = _.map(title.split(/\s*<LINE>\s*/g, utils.sanitize).join("<br>"));
|
||||||
btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}'));
|
|
||||||
|
return "Custom Command" + (title.indexOf('<br>') !== -1 ? 's' : '') +
|
||||||
|
"<br>" + title;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
|
||||||
btn.addEventListener('click', add_btn_click.bind(this, cmd));
|
btn.addEventListener('click', add_btn_click.bind(this, cmd));
|
||||||
return btn;
|
return btn;
|
||||||
};
|
};
|
||||||
|
|
||||||
var cmds = {};
|
|
||||||
for(var i=0; i < f.settings.mod_card_buttons.length; i++)
|
|
||||||
cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] = (cmds[f.settings.mod_card_buttons[i].split(' ',1)[0]] || 0) + 1;
|
|
||||||
|
|
||||||
for(var i=0; i < f.settings.mod_card_buttons.length; i++) {
|
for(var i=0; i < f.settings.mod_card_buttons.length; i++) {
|
||||||
var cmd = f.settings.mod_card_buttons[i],
|
var label, cmd, pair = f.settings.mod_card_buttons[i];
|
||||||
ind = cmd.indexOf('{user}');
|
if ( ! Array.isArray(pair) ) {
|
||||||
|
cmd = pair;
|
||||||
|
label = cmd.split(' ', 1)[0];
|
||||||
|
} else {
|
||||||
|
label = pair[0];
|
||||||
|
cmd = pair[1];
|
||||||
|
}
|
||||||
|
|
||||||
if ( ind === -1 )
|
utils.CMD_VAR_REGEX.lastIndex = 0;
|
||||||
cmd += ' {user}';
|
if ( ! utils.CMD_VAR_REGEX.test(cmd) ) {
|
||||||
|
var lines = cmd.split(/\s*<LINE>\s*/g);
|
||||||
|
lines[0] += ' {user}';
|
||||||
|
cmd = lines.join("<LINE>");
|
||||||
|
}
|
||||||
|
|
||||||
line.appendChild(add_btn_make(cmd))
|
line.appendChild(add_btn_make(label, cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
el.appendChild(line);
|
el.appendChild(line);
|
||||||
|
@ -1094,7 +1143,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( user_history.length < 50 ) {
|
if ( user_history.length < 50 ) {
|
||||||
var before = (user_history.length > 0 ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0);
|
var before = (user_history.length > 0 && user_history[0].date ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0);
|
||||||
f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) {
|
f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) {
|
||||||
if ( ! success )
|
if ( ! success )
|
||||||
return;
|
return;
|
||||||
|
@ -1271,9 +1320,9 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
|
||||||
colors = raw_color && this._handle_color(raw_color),
|
colors = raw_color && this._handle_color(raw_color),
|
||||||
|
|
||||||
Layout = utils.ember_lookup('service:layout'),
|
Layout = utils.ember_lookup('service:layout'),
|
||||||
Settings = utils.ember_lookup('controller:settings'),
|
Settings = utils.ember_settings(),
|
||||||
|
|
||||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
|
is_dark = (Layout && Layout.get('isTheatreMode')) || this.settings.get_twitch("darkMode");
|
||||||
|
|
||||||
|
|
||||||
// Styling
|
// Styling
|
||||||
|
|
|
@ -12,17 +12,28 @@ var FFZ = window.FrankerFaceZ,
|
||||||
'subscribers': 'subs_on',
|
'subscribers': 'subs_on',
|
||||||
'subscribersoff': 'subs_off',
|
'subscribersoff': 'subs_off',
|
||||||
'emoteonly': 'emote_only_on',
|
'emoteonly': 'emote_only_on',
|
||||||
'emoteonlyoff': 'emote_only_off'
|
'emoteonlyoff': 'emote_only_off',
|
||||||
|
'host': 'host_on',
|
||||||
|
'unhost': 'host_off'
|
||||||
},
|
},
|
||||||
|
|
||||||
STATUS_BADGES = [
|
STATUS_BADGES = [
|
||||||
["r9k", "r9k", "This room is in R9K-mode."],
|
["r9k", "r9k", "This room is in R9K-mode."],
|
||||||
["emote", "emoteOnly", "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode."],
|
["emote", "emoteOnly", "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode."],
|
||||||
["sub", "subsOnly", "This room is in subscribers-only mode."],
|
["sub", "subsOnly", "This room is in subscribers-only mode."],
|
||||||
["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow') || 120) + " seconds." }],
|
["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every <nobr>" + utils.number_commas(room && room.get('slow') || 120) + " seconds</nobr>." }],
|
||||||
["ban", "ffz_banned", "You have been banned from talking in this room."],
|
["ban", "ffz_banned", "You have been banned from talking in this room."],
|
||||||
["delay", function(room) { return room && room.get('ffz_chat_delay') !== 0 }, function(room) { return "Artificial chat delay is enabled. Messages are displayed after " + (room ? room.get('ffz_chat_delay')/1000 : 0) + " seconds." }],
|
["delay", function(room) {
|
||||||
["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in " + (this.settings.chat_batching/1000) + " second increments." }]
|
return room && (this.settings.chat_delay === -1 ?
|
||||||
|
room.get('roomProperties.chat_delay_duration')
|
||||||
|
: room.get('ffz_chat_delay'))
|
||||||
|
}, function(room) {
|
||||||
|
var is_mod = this.settings.chat_delay === -1;
|
||||||
|
return "Artificial chat delay is enabled" + (is_mod ? " for this channel" : "") + ". Messages are displayed after " + (room ? (is_mod ? room.get('roomProperties.chat_delay_duration') : room.get('ffz_chat_delay')/1000) : 0) + " seconds" + (is_mod ? " for <nobr>non-moderators</nobr>." : ".");
|
||||||
|
}, null, function(room) {
|
||||||
|
return room && this.settings.chat_delay === -1 && room.get('isModeratorOrHigher') || false;
|
||||||
|
}],
|
||||||
|
["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in <nobr>" + (this.settings.chat_batching/1000) + " second</nobr> increments." }]
|
||||||
],
|
],
|
||||||
|
|
||||||
// StrimBagZ Support
|
// StrimBagZ Support
|
||||||
|
@ -32,7 +43,7 @@ var FFZ = window.FrankerFaceZ,
|
||||||
if ( ! room.moderator_badge )
|
if ( ! room.moderator_badge )
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room.moderator_badge + '") !important; }';
|
return '.from-display-preview[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement),.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room.moderator_badge + '") !important; }';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,14 +167,14 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
|
||||||
token = user.chat_oauth_token,
|
token = user.chat_oauth_token,
|
||||||
new_topics = [
|
new_topics = [
|
||||||
"chat_message_updated." + room_id,
|
"chat_message_updated." + room_id,
|
||||||
"chat_moderator_actions." + room_id];
|
"chat_moderator_actions." + user.id + "." + room_id];
|
||||||
|
|
||||||
for(var i=0; i < new_topics.length; i++)
|
for(var i=0; i < new_topics.length; i++)
|
||||||
ps.Listen({
|
ps.Listen({
|
||||||
topic: new_topics[i],
|
topic: new_topics[i],
|
||||||
auth: token,
|
auth: token,
|
||||||
success: function() {},
|
success: function() {},
|
||||||
failure: function() {},
|
failure: function(t) { f.log("[PubSub] Failed to listen to topic: " + new_topics[i], t); },
|
||||||
message: Ember.run.bind(n, n._onPubsubMessage, new_topics[i])
|
message: Ember.run.bind(n, n._onPubsubMessage, new_topics[i])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -199,7 +210,7 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
|
||||||
ps.Unlisten({
|
ps.Unlisten({
|
||||||
topic: old_topics[i],
|
topic: old_topics[i],
|
||||||
success: function() {},
|
success: function() {},
|
||||||
failure: function() {}
|
failure: function(t) { f.log("[PubSub] Failed to unlisten to topic: " + old_topics[i], t); }
|
||||||
});
|
});
|
||||||
this.chatTopics.removeObject(old_topics[i]);
|
this.chatTopics.removeObject(old_topics[i]);
|
||||||
}
|
}
|
||||||
|
@ -238,14 +249,14 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
|
||||||
ps.Unlisten({
|
ps.Unlisten({
|
||||||
topic: pubsub.chatTopics[i],
|
topic: pubsub.chatTopics[i],
|
||||||
success: function() {},
|
success: function() {},
|
||||||
failure: function() {}
|
failure: function(t) { f.log("[PubSub] Failed to unlisten to topic: " + old_topics[i], t); }
|
||||||
});
|
});
|
||||||
|
|
||||||
ps.Listen({
|
ps.Listen({
|
||||||
topic: pubsub.chatTopics[i],
|
topic: pubsub.chatTopics[i],
|
||||||
auth: token,
|
auth: token,
|
||||||
success: function() {},
|
success: function() {},
|
||||||
failure: function() {},
|
failure: function(t) { f.log("[PubSub] Failed to listen to topic: " + new_topics[i], t); },
|
||||||
message: Ember.run.bind(pubsub, pubsub._onPubsubMessage, pubsub.chatTopics[i])
|
message: Ember.run.bind(pubsub, pubsub._onPubsubMessage, pubsub.chatTopics[i])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -424,7 +435,7 @@ FFZ.prototype.modify_room_view = function(view) {
|
||||||
if ( ! badge ) {
|
if ( ! badge ) {
|
||||||
badge = utils.createElement('span', 'ffz room-state stat float-right', (label || info[0]).charAt(0).toUpperCase() + '<span>' + (label || info[0]).substr(1).toUpperCase() + '</span>');
|
badge = utils.createElement('span', 'ffz room-state stat float-right', (label || info[0]).charAt(0).toUpperCase() + '<span>' + (label || info[0]).substr(1).toUpperCase() + '</span>');
|
||||||
badge.id = id;
|
badge.id = id;
|
||||||
jQuery(badge).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
|
jQuery(badge).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
|
||||||
cont.appendChild(badge);
|
cont.appendChild(badge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +444,7 @@ FFZ.prototype.modify_room_view = function(view) {
|
||||||
|
|
||||||
badge.title = typeof info[2] === "function" ? info[2].call(f, room) : info[2];
|
badge.title = typeof info[2] === "function" ? info[2].call(f, room) : info[2];
|
||||||
badge.classList.toggle('hidden', ! visible);
|
badge.classList.toggle('hidden', ! visible);
|
||||||
|
badge.classList.toggle('faded', info[4] !== undefined ? typeof info[4] === "function" ? info[4].call(f, room) : info[4] : false);
|
||||||
if ( visible )
|
if ( visible )
|
||||||
vis_count++;
|
vis_count++;
|
||||||
}
|
}
|
||||||
|
@ -576,7 +588,8 @@ FFZ.prototype.modify_room_view = function(view) {
|
||||||
var e = this,
|
var e = this,
|
||||||
s = this._$chatMessagesScroller;
|
s = this._$chatMessagesScroller;
|
||||||
|
|
||||||
Ember.run.next(function() {
|
//this.runTask(function() {
|
||||||
|
Ember.run.next(function(){
|
||||||
// Trying random performance tweaks for fun and profit!
|
// Trying random performance tweaks for fun and profit!
|
||||||
(window.requestAnimationFrame||setTimeout)(function(){
|
(window.requestAnimationFrame||setTimeout)(function(){
|
||||||
if ( e.ffz_frozen || ! s || ! s.length )
|
if ( e.ffz_frozen || ! s || ! s.length )
|
||||||
|
@ -584,13 +597,16 @@ FFZ.prototype.modify_room_view = function(view) {
|
||||||
|
|
||||||
s[0].scrollTop = s[0].scrollHeight;
|
s[0].scrollTop = s[0].scrollHeight;
|
||||||
e._setStuckToBottom(true);
|
e._setStuckToBottom(true);
|
||||||
|
e._tagVisibleMessages();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, 200),
|
}, 200),
|
||||||
|
|
||||||
_setStuckToBottom: function(val) {
|
_setStuckToBottom: function(val) {
|
||||||
this.set("stuckToBottom", val);
|
this.set("stuckToBottom", val);
|
||||||
this.get("controller.model") && this.set("controller.model.messageBufferSize", f.settings.scrollback_length + (val ? 0 : 150));
|
var model = this.get("controller.model");
|
||||||
|
if ( model )
|
||||||
|
model.messageBufferSize = f.settings.scrollback_length + (val ? 0 : 150);
|
||||||
if ( ! val )
|
if ( ! val )
|
||||||
this.ffzUnfreeze(true);
|
this.ffzUnfreeze(true);
|
||||||
},
|
},
|
||||||
|
@ -649,15 +665,13 @@ FFZ.ffz_commands = {};
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.room_message = function(room, text) {
|
FFZ.prototype.room_message = function(room, text) {
|
||||||
var lines = text.split("\n");
|
|
||||||
if ( this.has_bttv ) {
|
if ( this.has_bttv ) {
|
||||||
|
var lines = text.split("\n");
|
||||||
for(var i=0; i < lines.length; i++)
|
for(var i=0; i < lines.length; i++)
|
||||||
BetterTTV.chat.handlers.onPrivmsg(room.id, {style: 'admin', date: new Date(), from: 'jtv', message: lines[i]});
|
BetterTTV.chat.handlers.onPrivmsg(room.id, {style: 'admin', date: new Date(), from: 'jtv', message: lines[i]});
|
||||||
|
|
||||||
} else {
|
} else
|
||||||
for(var i=0; i < lines.length; i++)
|
room.room.addMessage({ffz_line_returns: true, style: 'ffz admin', date: new Date(), from: 'FFZ', message: text});
|
||||||
room.room.addMessage({style: 'ffz admin', date: new Date(), from: 'FFZ', message: lines[i]});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -821,10 +835,10 @@ FFZ.prototype.add_room = function(id, room) {
|
||||||
this.ws_sub("room." + id);
|
this.ws_sub("room." + id);
|
||||||
|
|
||||||
// Do we want history?
|
// Do we want history?
|
||||||
if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) {
|
/*if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) {
|
||||||
if ( ! this.ws_send("chat_history", [id,25], this._load_history.bind(this, id)) )
|
if ( ! this.ws_send("chat_history", [id,25], this._load_history.bind(this, id)) )
|
||||||
data.needs_history = true;
|
data.needs_history = true;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -874,7 +888,7 @@ FFZ.prototype.remove_room = function(id) {
|
||||||
// Chat History
|
// Chat History
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
FFZ.prototype._load_history = function(room_id, success, data) {
|
/*FFZ.prototype._load_history = function(room_id, success, data) {
|
||||||
var room = this.rooms[room_id];
|
var room = this.rooms[room_id];
|
||||||
if ( ! room || ! room.room )
|
if ( ! room || ! room.room )
|
||||||
return;
|
return;
|
||||||
|
@ -888,7 +902,7 @@ FFZ.prototype._load_history = function(room_id, success, data) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return this._insert_history(room_id, data, true);
|
return this._insert_history(room_id, data, true);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype._show_deleted = function(room_id) {
|
FFZ.prototype._show_deleted = function(room_id) {
|
||||||
|
@ -1224,6 +1238,9 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( ! f.settings.get_twitch('showModerationActions') )
|
||||||
|
return;
|
||||||
|
|
||||||
var target_notice = NOTICE_MAPPING[event.moderation_action];
|
var target_notice = NOTICE_MAPPING[event.moderation_action];
|
||||||
if ( target_notice ) {
|
if ( target_notice ) {
|
||||||
var last_notice = this.ffz_last_notices && this.ffz_last_notices[target_notice];
|
var last_notice = this.ffz_last_notices && this.ffz_last_notices[target_notice];
|
||||||
|
@ -1233,13 +1250,13 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
last_notice.has_owner = true;
|
last_notice.has_owner = true;
|
||||||
last_notice.cachedTokens = undefined;
|
last_notice.cachedTokens = undefined;
|
||||||
if ( last_notice._line )
|
if ( last_notice._line )
|
||||||
last_notice._line.ffzRender();
|
Ember.propertyDidChange(last_notice._line, 'ffzTokenizedMessage');
|
||||||
} else {
|
} else {
|
||||||
var waiting = this.ffz_waiting_notices = this.ffz_waiting_notices || {};
|
var waiting = this.ffz_waiting_notices = this.ffz_waiting_notices || {};
|
||||||
waiting[target_notice] = event.created_by;
|
waiting[target_notice] = event.created_by;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( f.settings.get_twitch('showModerationActions') )
|
} else
|
||||||
this._super(event);
|
this._super(event);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1249,6 +1266,8 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// f.log("Login Moderation", event);
|
||||||
|
|
||||||
// In case we get unexpected input, do the other thing.
|
// In case we get unexpected input, do the other thing.
|
||||||
if ( ["ban", "unban", "timeout"].indexOf(event.moderation_action) === -1 )
|
if ( ["ban", "unban", "timeout"].indexOf(event.moderation_action) === -1 )
|
||||||
return this._super(event);
|
return this._super(event);
|
||||||
|
@ -1259,7 +1278,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
'ban-moderator': event.created_by
|
'ban-moderator': event.created_by
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clearMessages(event.args[0], tags, false, event.moderation_action !== 'unban');
|
this.clearMessages(event.args[0].toLowerCase(), tags, false, event.moderation_action !== 'unban');
|
||||||
},
|
},
|
||||||
|
|
||||||
clearMessages: function(user, tags, disable_log, report_only) {
|
clearMessages: function(user, tags, disable_log, report_only) {
|
||||||
|
@ -1424,6 +1443,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
// Look up the user's last ban.
|
// Look up the user's last ban.
|
||||||
var show_notice = is_me || this.ffzShouldDisplayNotice(),
|
var show_notice = is_me || this.ffzShouldDisplayNotice(),
|
||||||
show_reason = is_me || this.get('isModeratorOrHigher'),
|
show_reason = is_me || this.get('isModeratorOrHigher'),
|
||||||
|
show_moderator = f.settings.get_twitch('showModerationActions'),
|
||||||
room = f.rooms && f.rooms[t.get('id')],
|
room = f.rooms && f.rooms[t.get('id')],
|
||||||
now = new Date,
|
now = new Date,
|
||||||
end_time = now + (duration * 1000),
|
end_time = now + (duration * 1000),
|
||||||
|
@ -1457,7 +1477,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
durations: [duration],
|
durations: [duration],
|
||||||
end_time: end_time,
|
end_time: end_time,
|
||||||
timeouts: report_only ? 0 : 1,
|
timeouts: report_only ? 0 : 1,
|
||||||
message: message + (show_reason && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
|
message: message + (show_reason && show_moderator && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( ban_history )
|
if ( ban_history )
|
||||||
|
@ -1485,7 +1505,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
last_ban.message = message +
|
last_ban.message = message +
|
||||||
(last_ban.timeouts > 1 ? ' (' + utils.number_commas(last_ban.timeouts) + ' times)' : '') +
|
(last_ban.timeouts > 1 ? ' (' + utils.number_commas(last_ban.timeouts) + ' times)' : '') +
|
||||||
(!show_reason || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
|
(!show_reason || !show_moderator || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
|
||||||
(!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '));
|
(!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '));
|
||||||
last_ban.cachedTokens = [{type: "text", text: last_ban.message}];
|
last_ban.cachedTokens = [{type: "text", text: last_ban.message}];
|
||||||
|
|
||||||
|
@ -1556,7 +1576,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
trimMessages: function() {
|
/*trimMessages: function() {
|
||||||
var messages = this.get("messages"),
|
var messages = this.get("messages"),
|
||||||
len = messages.get("length"),
|
len = messages.get("length"),
|
||||||
limit = this.get("messageBufferSize");
|
limit = this.get("messageBufferSize");
|
||||||
|
@ -1578,7 +1598,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
messages.removeAt(0, to_remove);
|
messages.removeAt(0, to_remove);
|
||||||
}
|
}
|
||||||
},
|
},*/
|
||||||
|
|
||||||
// Artificial chat delay
|
// Artificial chat delay
|
||||||
ffz_chat_delay: function() {
|
ffz_chat_delay: function() {
|
||||||
|
@ -1608,14 +1628,81 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
this.ffzSchedulePendingFlush(now);
|
this.ffzSchedulePendingFlush(now);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.ffzActualPushMessage(msg);
|
this.ffzPushMessages([msg]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzActualPushMessage: function (msg) {
|
ffzPushMessages: function(messages) {
|
||||||
|
var new_messages = [],
|
||||||
|
new_unread = 0;
|
||||||
|
|
||||||
|
for(var i=0; i < messages.length; i++) {
|
||||||
|
var msg = messages[i];
|
||||||
|
if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
|
||||||
|
new_messages.push(msg);
|
||||||
|
|
||||||
|
if ( ! (msg.tags && msg.tags.historical) && msg.style !== "admin" && msg.style !== "whisper" ) {
|
||||||
|
if ( msg.ffz_has_mention )
|
||||||
|
this.ffz_last_mention = Date.now();
|
||||||
|
|
||||||
|
new_unread++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! new_messages.length )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var room_messages = this.get("messages"),
|
||||||
|
trimmed = room_messages.length + new_messages.length > this.messageBufferSize ?
|
||||||
|
room_messages.slice(Math.max(0, (room_messages.length - this.messageBufferSize)) + new_messages.length, room_messages.length) :
|
||||||
|
room_messages.slice(0, room_messages.length);
|
||||||
|
|
||||||
|
var earliest_message;
|
||||||
|
for(var i=0; i < trimmed.length; i++)
|
||||||
|
if ( trimmed[i].date ) {
|
||||||
|
earliest_message = trimmed[i].date;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < new_messages.length; i++) {
|
||||||
|
var msg = new_messages[i];
|
||||||
|
if ( msg.tags && msg.tags.historical ) {
|
||||||
|
// Add a warning about really old messages.
|
||||||
|
if ( earliest_message ) {
|
||||||
|
var age = earliest_message - msg.date;
|
||||||
|
if ( age > 300000 )
|
||||||
|
trimmed.unshift({
|
||||||
|
color: '#755000',
|
||||||
|
date: msg.date,
|
||||||
|
from: 'frankerfacez_admin',
|
||||||
|
style: 'admin',
|
||||||
|
message: '(Last message is ' + utils.human_time(age/1000) + ' old.)',
|
||||||
|
room: msg.room,
|
||||||
|
from_server: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trimmed.unshift(msg);
|
||||||
|
earliest_message = null;
|
||||||
|
|
||||||
|
} else
|
||||||
|
trimmed.push(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set("messages", trimmed);
|
||||||
|
|
||||||
|
if ( new_unread ) {
|
||||||
|
this.incrementProperty("unreadCount", new_unread);
|
||||||
|
this.ffz_last_activity = Date.now();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*ffzActualPushMessage: function (msg) {
|
||||||
if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
|
if ( this.shouldShowMessage(msg) && this.ffzShouldShowMessage(msg) ) {
|
||||||
this.get("messages").pushObject(msg);
|
this.get("messages").pushObject(msg);
|
||||||
this.trimMessages();
|
this.trimMessages();
|
||||||
|
Ember.propertyDidChange(this, "messages");
|
||||||
|
|
||||||
if ( msg.style !== "admin" && msg.style !== "whisper" ) {
|
if ( msg.style !== "admin" && msg.style !== "whisper" ) {
|
||||||
if ( msg.ffz_has_mention ) {
|
if ( msg.ffz_has_mention ) {
|
||||||
|
@ -1626,7 +1713,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
this.incrementProperty("unreadCount", 1);
|
this.incrementProperty("unreadCount", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},*/
|
||||||
|
|
||||||
ffzSchedulePendingFlush: function(now) {
|
ffzSchedulePendingFlush: function(now) {
|
||||||
// Instead of just blindly looping every x seconds, we want to calculate the time until
|
// Instead of just blindly looping every x seconds, we want to calculate the time until
|
||||||
|
@ -1655,7 +1742,8 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
this._ffz_pending_flush = null;
|
this._ffz_pending_flush = null;
|
||||||
|
|
||||||
var now = this._ffz_last_batch = Date.now(),
|
var now = this._ffz_last_batch = Date.now(),
|
||||||
chat_delay = this.get('ffz_chat_delay');
|
chat_delay = this.get('ffz_chat_delay'),
|
||||||
|
to_display = [];
|
||||||
|
|
||||||
for (var i = 0, l = this.ffzPending.length; i < l; i++) {
|
for (var i = 0, l = this.ffzPending.length; i < l; i++) {
|
||||||
var msg = this.ffzPending[i];
|
var msg = this.ffzPending[i];
|
||||||
|
@ -1677,9 +1765,11 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
msg.pending = false;
|
msg.pending = false;
|
||||||
this.ffzActualPushMessage(msg);
|
to_display.push(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.ffzPushMessages(to_display);
|
||||||
|
|
||||||
this.ffzPending = this.ffzPending.slice(i);
|
this.ffzPending = this.ffzPending.slice(i);
|
||||||
this.ffzSchedulePendingFlush(now);
|
this.ffzSchedulePendingFlush(now);
|
||||||
},
|
},
|
||||||
|
@ -1714,7 +1804,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( (msg.msgId === 'timeout_success' || msg.msgId === 'ban_success') && this.ffzShouldDisplayNotice() )
|
if ( (msg.msgId === 'timeout_success' || msg.msgId === 'ban_success') && this.ffzShouldDisplayNotice() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
f.log("Notification", msg);
|
// f.log("Notification", msg);
|
||||||
|
|
||||||
if ( ! msg.tags )
|
if ( ! msg.tags )
|
||||||
msg.tags = {};
|
msg.tags = {};
|
||||||
|
@ -1740,7 +1830,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
this.addMessage(msg);
|
this.addMessage(msg);
|
||||||
},
|
},
|
||||||
|
|
||||||
addMessage: function(msg) {
|
ffzProcessMessage: function(msg) {
|
||||||
if ( msg ) {
|
if ( msg ) {
|
||||||
var notice_type = msg.tags && msg.tags['msg-id'],
|
var notice_type = msg.tags && msg.tags['msg-id'],
|
||||||
is_resub = notice_type === 'resub',
|
is_resub = notice_type === 'resub',
|
||||||
|
@ -1769,6 +1859,13 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
msg.tags['system-msg'] = '';
|
msg.tags['system-msg'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix dates for historical messages.
|
||||||
|
if ( ! msg.date && msg.tags && msg.tags['tmi-sent-ts'] ) {
|
||||||
|
var sent = parseInt(msg.tags['tmi-sent-ts']);
|
||||||
|
if ( sent && ! isNaN(sent) && isFinite(sent) )
|
||||||
|
msg.date = new Date(sent);
|
||||||
|
}
|
||||||
|
|
||||||
var is_whisper = msg.style === 'whisper';
|
var is_whisper = msg.style === 'whisper';
|
||||||
|
|
||||||
// Ignore whispers if conversations are enabled.
|
// Ignore whispers if conversations are enabled.
|
||||||
|
@ -1792,7 +1889,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
// Tokenization
|
// Tokenization
|
||||||
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
||||||
|
|
||||||
// If it's from Twitch notify, and it's directly related to
|
// Check for a new subscription line that would need a chat badge.
|
||||||
if ( msg.from === 'twitchnotify' && msg.message.indexOf('subscribed to') === -1 && msg.message.indexOf('subscribed') !== -1 ) {
|
if ( msg.from === 'twitchnotify' && msg.message.indexOf('subscribed to') === -1 && msg.message.indexOf('subscribed') !== -1 ) {
|
||||||
if ( ! msg.tags )
|
if ( ! msg.tags )
|
||||||
msg.tags = {};
|
msg.tags = {};
|
||||||
|
@ -1804,6 +1901,29 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
msg.labels.push("subscriber");
|
msg.labels.push("subscriber");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Color processing.
|
||||||
|
if ( msg.color )
|
||||||
|
f._handle_color(msg.color);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addMessage: function(msg) {
|
||||||
|
msg = this.ffzProcessMessage(msg);
|
||||||
|
if ( ! msg )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var msg_id = msg.tags && msg.tags.id,
|
||||||
|
notice_type = msg.tags && msg.tags['msg-id'],
|
||||||
|
is_whisper = msg.style === 'whisper';
|
||||||
|
|
||||||
|
|
||||||
|
// If this message is already in the room, discard the duplicate.
|
||||||
|
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
// Keep the history.
|
// Keep the history.
|
||||||
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
|
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
|
||||||
var room = f.rooms && f.rooms[msg.room];
|
var room = f.rooms && f.rooms[msg.room];
|
||||||
|
@ -1824,6 +1944,12 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
date: msg.date
|
date: msg.date
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ( msg.tags && msg.tags.historical ) {
|
||||||
|
// If it's historical, insert it at the beginning. And stuff.
|
||||||
|
if ( user_history.length < 20 )
|
||||||
|
user_history.unshift(new_msg);
|
||||||
|
|
||||||
|
} else {
|
||||||
// Preserve message order if we *just* received a ban.
|
// Preserve message order if we *just* received a ban.
|
||||||
if ( last_history && last_history.is_delete && (msg.date - last_history.date) <= 200 ) {
|
if ( last_history && last_history.is_delete && (msg.date - last_history.date) <= 200 ) {
|
||||||
user_history.splice(user_history.length - 1, 0, new_msg);
|
user_history.splice(user_history.length - 1, 0, new_msg);
|
||||||
|
@ -1832,6 +1958,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
if ( user_history.length > 20 )
|
if ( user_history.length > 20 )
|
||||||
user_history.shift();
|
user_history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
|
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
|
||||||
var el = f._mod_card.get('element'),
|
var el = f._mod_card.get('element'),
|
||||||
|
@ -1839,7 +1966,11 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
|
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
|
||||||
|
|
||||||
if ( history ) {
|
if ( history ) {
|
||||||
history.appendChild(f._build_mod_card_history(msg, f._mod_card));
|
var el = f._build_mod_card_history(msg, f._mod_card);
|
||||||
|
if ( msg.tags && msg.tags.historical )
|
||||||
|
history.insertBefore(el, history.firstElementChild);
|
||||||
|
else
|
||||||
|
history.appendChild(el);
|
||||||
if ( was_at_top )
|
if ( was_at_top )
|
||||||
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
||||||
|
|
||||||
|
@ -1881,14 +2012,6 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also update chatters.
|
|
||||||
if ( ! is_whisper && this.chatters && ! this.chatters[msg.from] && msg.from !== 'twitchnotify' && msg.from !== 'jtv' )
|
|
||||||
this.ffzUpdateChatters(msg.from);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color processing.
|
|
||||||
if ( msg.color )
|
|
||||||
f._handle_color(msg.color);
|
|
||||||
|
|
||||||
// Message Filtering
|
// Message Filtering
|
||||||
var i = f._chat_filters.length;
|
var i = f._chat_filters.length;
|
||||||
|
@ -1896,6 +2019,11 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( f._chat_filters[i](msg) === false )
|
if ( f._chat_filters[i](msg) === false )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
// Also update chatters.
|
||||||
|
if ( ! is_whisper && this.chatters && ! this.chatters[msg.from] && msg.from !== 'twitchnotify' && msg.from !== 'jtv' )
|
||||||
|
this.ffzUpdateChatters(msg.from);
|
||||||
|
|
||||||
// We're past the last return, so store the message
|
// We're past the last return, so store the message
|
||||||
// now that we know we're keeping it.
|
// now that we know we're keeping it.
|
||||||
if ( msg_id ) {
|
if ( msg_id ) {
|
||||||
|
@ -1913,8 +2041,15 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
|
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
|
||||||
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, "*"); //location.protocol + "//www.twitch.tv/");
|
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, "*"); //location.protocol + "//www.twitch.tv/");
|
||||||
|
|
||||||
// Add the message.
|
// Flagging for review.
|
||||||
return this._super(msg);
|
if ( msg.tags && msg.tags.risk === "high" )
|
||||||
|
msg.flaggedForReview = true;
|
||||||
|
|
||||||
|
// Add the message. We don't do super anymore because it does stupid stuff.'
|
||||||
|
this.pushMessage(msg);
|
||||||
|
msg.from && msg.style !== "admin" && msg.style !== "notification" && msg.tags && this.addChatter(msg);
|
||||||
|
this.trackLatency(msg);
|
||||||
|
//return this._super(msg);
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzChatFilters: function(msg) {
|
ffzChatFilters: function(msg) {
|
||||||
|
@ -1934,7 +2069,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
return this._super(e);
|
return this._super(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
send: function(text, ignore_history) {
|
send: function(text, ignore_history, used_aliases) {
|
||||||
try {
|
try {
|
||||||
this.ffz_last_input = Date.now();
|
this.ffz_last_input = Date.now();
|
||||||
|
|
||||||
|
@ -1958,16 +2093,42 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
mru.unshift(text);
|
mru.unshift(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd = text.split(' ', 1)[0].toLowerCase();
|
if ( is_cmd ) {
|
||||||
if ( cmd === "/ffz" ) {
|
var cmd = text.substr(1).split(' ', 1)[0].toLowerCase(),
|
||||||
this.set("messageToSend", "");
|
was_handled = false;
|
||||||
f.run_ffz_command(text.substr(5), this.get('id'));
|
|
||||||
return;
|
|
||||||
|
|
||||||
} else if ( cmd.charAt(0) === "/" && f.run_command(text, this.get('id')) ) {
|
if ( cmd === "ffz" ) {
|
||||||
|
f.run_ffz_command(text.substr(5), this.get('id'));
|
||||||
|
was_handled = true;
|
||||||
|
|
||||||
|
} else if ( f._command_aliases[cmd] ) {
|
||||||
|
used_aliases = used_aliases || [];
|
||||||
|
if ( used_aliases.indexOf(cmd) !== -1 ) {
|
||||||
|
f.room_message(f.rooms[this.get('id')], "Error: Your command aliases are recursing. [Path: " + used_aliases.join(", ") + "]");
|
||||||
|
was_handled = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var alias = f._command_aliases[cmd],
|
||||||
|
args = text.substr(1 + cmd.length).trimLeft().split(/\s+/g),
|
||||||
|
output = utils.replace_cmd_variables(alias, null, this, null, args);
|
||||||
|
|
||||||
|
used_aliases.push(cmd);
|
||||||
|
this.set("messageToSend", "");
|
||||||
|
var lines = output.split(/\s*<LINE>\s*/g);
|
||||||
|
for(var i=0; i < lines.length; i++)
|
||||||
|
this.send(lines[i], true, used_aliases);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ( f.run_command(text, this.get('id')) )
|
||||||
|
was_handled = true;
|
||||||
|
|
||||||
|
if ( was_handled ) {
|
||||||
this.set("messageToSend", "");
|
this.set("messageToSend", "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
f.error("send: " + err);
|
f.error("send: " + err);
|
||||||
|
@ -2130,10 +2291,6 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
// Room State Stuff
|
// Room State Stuff
|
||||||
|
|
||||||
slowMode: function() {
|
|
||||||
return this.get('slow') > 0;
|
|
||||||
}.property('slow'),
|
|
||||||
|
|
||||||
onSlowOff: function() {
|
onSlowOff: function() {
|
||||||
if ( ! this.get('slowMode') )
|
if ( ! this.get('slowMode') )
|
||||||
this.updateWait(0);
|
this.updateWait(0);
|
||||||
|
|
|
@ -45,12 +45,12 @@ FFZ.prototype.setup_vod_chat = function() {
|
||||||
f.error("Unable to locate VOD Chat Service.");
|
f.error("Unable to locate VOD Chat Service.");
|
||||||
|
|
||||||
this.update_views('component:vod-right-column', this.modify_vod_right_column);
|
this.update_views('component:vod-right-column', this.modify_vod_right_column);
|
||||||
this.update_views('view:vod', this.modify_vod_view);
|
//this.update_views('view:vod', this.modify_vod_view);
|
||||||
this.update_views('component:vod-chat-display', this.modify_vod_chat_display);
|
this.update_views('component:vod-chat-display', this.modify_vod_chat_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.modify_vod_view = function(view) {
|
/*FFZ.prototype.modify_vod_view = function(view) {
|
||||||
var f = this;
|
var f = this;
|
||||||
utils.ember_reopen_view(view, {
|
utils.ember_reopen_view(view, {
|
||||||
ffz_init: function() {
|
ffz_init: function() {
|
||||||
|
@ -90,7 +90,7 @@ FFZ.prototype.modify_vod_view = function(view) {
|
||||||
document.body.classList.toggle('ffz-small-player', f.settings.small_player && top >= height);
|
document.body.classList.toggle('ffz-small-player', f.settings.small_player && top >= height);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.modify_vod_right_column = function(component) {
|
FFZ.prototype.modify_vod_right_column = function(component) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ FFZ.prototype.api = function(name, icon, version) {
|
||||||
try {
|
try {
|
||||||
this._known_apis = JSON.parse(localStorage.ffz_known_apis);
|
this._known_apis = JSON.parse(localStorage.ffz_known_apis);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log("Error loading Known APIs: " + err);
|
this.error("Error loading Known APIs", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +105,11 @@ API.prototype.log = function(msg, data, to_json, log_json) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.prototype.error = function(msg, error, to_json, log_json) {
|
||||||
|
this.ffz.error('Ext "' + this.name + '": ' + msg, data, to_json, log_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Set Loading
|
// Set Loading
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -115,8 +120,8 @@ API.prototype._load_set = function(real_id, set_id, data) {
|
||||||
|
|
||||||
// Check for an existing set to copy the users.
|
// Check for an existing set to copy the users.
|
||||||
var users = [];
|
var users = [];
|
||||||
if ( this.emote_sets[real_id] && this.emote_sets[real_id].users )
|
if ( this.emote_sets[set_id] && this.emote_sets[set_id].users )
|
||||||
users = this.emote_sets[real_id].users;
|
users = this.emote_sets[set_id].users;
|
||||||
|
|
||||||
var emote_set = {
|
var emote_set = {
|
||||||
source: this.name,
|
source: this.name,
|
||||||
|
@ -127,14 +132,16 @@ API.prototype._load_set = function(real_id, set_id, data) {
|
||||||
emoticons: {},
|
emoticons: {},
|
||||||
_type: data._type || 0,
|
_type: data._type || 0,
|
||||||
css: data.css || null,
|
css: data.css || null,
|
||||||
|
hidden: data.hidden || false,
|
||||||
description: data.description || null,
|
description: data.description || null,
|
||||||
icon: data.icon || this.icon || null,
|
icon: data.icon || this.icon || null,
|
||||||
id: real_id,
|
id: real_id,
|
||||||
title: data.title || "Global Emoticons",
|
title: data.title || "Global Emoticons",
|
||||||
};
|
};
|
||||||
|
|
||||||
this.emote_sets[real_id] = emote_set;
|
this.emote_sets[set_id] = emote_set;
|
||||||
|
|
||||||
|
// Use the real ID for FFZ's own tracking.
|
||||||
if ( this.ffz.emote_sets )
|
if ( this.ffz.emote_sets )
|
||||||
this.ffz.emote_sets[real_id] = emote_set;
|
this.ffz.emote_sets[real_id] = emote_set;
|
||||||
|
|
||||||
|
@ -202,6 +209,7 @@ API.prototype._load_set = function(real_id, set_id, data) {
|
||||||
emoticons[id] = new_emote;
|
emoticons[id] = new_emote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the real ID for building CSS.
|
||||||
utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || ""));
|
utils.update_css(this.ffz._emote_style, real_id, output_css + (emote_set.css || ""));
|
||||||
|
|
||||||
if ( this.ffz._cindex )
|
if ( this.ffz._cindex )
|
||||||
|
@ -231,21 +239,21 @@ API.prototype.load_set = function(id, emote_set) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
API.prototype.unload_set = function(id) {
|
API.prototype.unload_set = function(set_id) {
|
||||||
var exact_id = this.id + '-' + id,
|
var exact_id = this.id + '-' + set_id,
|
||||||
emote_set = this.emote_sets[exact_id];
|
emote_set = this.emote_sets[set_id];
|
||||||
|
|
||||||
if ( ! emote_set )
|
if ( ! emote_set )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// First, let's unregister it as a global.
|
// First, let's unregister it as a global.
|
||||||
this.unregister_global_set(id);
|
this.unregister_global_set(set_id);
|
||||||
|
|
||||||
|
|
||||||
// Now, remove the set data.
|
// Now, remove the set data.
|
||||||
utils.update_css(this.ffz._emote_style, exact_id, null);
|
utils.update_css(this.ffz._emote_style, exact_id, null);
|
||||||
|
|
||||||
this.emote_sets[exact_id] = undefined;
|
this.emote_sets[set_id] = undefined;
|
||||||
if ( this.ffz.emote_sets )
|
if ( this.ffz.emote_sets )
|
||||||
this.ffz.emote_sets[exact_id] = undefined;
|
this.ffz.emote_sets[exact_id] = undefined;
|
||||||
|
|
||||||
|
@ -272,9 +280,8 @@ API.prototype.unload_set = function(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
API.prototype.get_set = function(id) {
|
API.prototype.get_set = function(set_id) {
|
||||||
var exact_id = this.id + '-' + id;
|
return this.emote_sets[set_id];
|
||||||
return this.emote_sets[exact_id];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,14 +289,14 @@ API.prototype.get_set = function(id) {
|
||||||
// Global Emote Sets
|
// Global Emote Sets
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
API.prototype.register_global_set = function(id, emote_set) {
|
API.prototype.register_global_set = function(set_id, emote_set) {
|
||||||
var exact_id = this.id + '-' + id;
|
var exact_id = this.id + '-' + set_id;
|
||||||
|
|
||||||
if ( emote_set ) {
|
if ( emote_set ) {
|
||||||
// If a set was provided, load it.
|
// If a set was provided, load it.
|
||||||
emote_set = this.load_set(id, emote_set);
|
emote_set = this.load_set(set_id, emote_set);
|
||||||
} else
|
} else
|
||||||
emote_set = this.emote_sets[exact_id];
|
emote_set = this.emote_sets[set_id];
|
||||||
|
|
||||||
if ( ! emote_set )
|
if ( ! emote_set )
|
||||||
throw new Error("Invalid set ID");
|
throw new Error("Invalid set ID");
|
||||||
|
@ -301,11 +308,11 @@ API.prototype.register_global_set = function(id, emote_set) {
|
||||||
|
|
||||||
|
|
||||||
// It's a valid set if we get here, so make it global.
|
// It's a valid set if we get here, so make it global.
|
||||||
if ( this.global_sets.indexOf(exact_id) === -1 )
|
if ( this.global_sets.indexOf(set_id) === -1 )
|
||||||
this.global_sets.push(exact_id);
|
this.global_sets.push(set_id);
|
||||||
|
|
||||||
if ( this.default_sets.indexOf(exact_id) === -1 )
|
if ( this.default_sets.indexOf(set_id) === -1 )
|
||||||
this.default_sets.push(exact_id);
|
this.default_sets.push(set_id);
|
||||||
|
|
||||||
if ( this.ffz.global_sets && this.ffz.global_sets.indexOf(exact_id) === -1 )
|
if ( this.ffz.global_sets && this.ffz.global_sets.indexOf(exact_id) === -1 )
|
||||||
this.ffz.global_sets.push(exact_id);
|
this.ffz.global_sets.push(exact_id);
|
||||||
|
@ -319,19 +326,19 @@ API.prototype.register_global_set = function(id, emote_set) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
API.prototype.unregister_global_set = function(id) {
|
API.prototype.unregister_global_set = function(set_id) {
|
||||||
var exact_id = this.id + '-' + id,
|
var exact_id = this.id + '-' + set_id,
|
||||||
emote_set = this.emote_sets[exact_id];
|
emote_set = this.emote_sets[set_id];
|
||||||
|
|
||||||
if ( ! emote_set )
|
if ( ! emote_set )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove the set from global sets.
|
// Remove the set from global sets.
|
||||||
var ind = this.global_sets.indexOf(exact_id);
|
var ind = this.global_sets.indexOf(set_id);
|
||||||
if ( ind !== -1 )
|
if ( ind !== -1 )
|
||||||
this.global_sets.splice(ind,1);
|
this.global_sets.splice(ind,1);
|
||||||
|
|
||||||
ind = this.default_sets.indexOf(exact_id);
|
ind = this.default_sets.indexOf(set_id);
|
||||||
if ( ind !== -1 )
|
if ( ind !== -1 )
|
||||||
this.default_sets.splice(ind,1);
|
this.default_sets.splice(ind,1);
|
||||||
|
|
||||||
|
@ -353,8 +360,8 @@ API.prototype.unregister_global_set = function(id) {
|
||||||
// Per-Channel Emote Sets
|
// Per-Channel Emote Sets
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
|
||||||
API.prototype.register_room_set = function(room_id, id, emote_set) {
|
API.prototype.register_room_set = function(room_id, set_id, emote_set) {
|
||||||
var exact_id = this.id + '-' + id,
|
var exact_id = this.id + '-' + set_id,
|
||||||
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
||||||
|
|
||||||
if ( ! room )
|
if ( ! room )
|
||||||
|
@ -365,9 +372,9 @@ API.prototype.register_room_set = function(room_id, id, emote_set) {
|
||||||
emote_set.title = emote_set.title || "Channel: " + (room.display_name || room_id);
|
emote_set.title = emote_set.title || "Channel: " + (room.display_name || room_id);
|
||||||
emote_set._type = emote_set._type || 1;
|
emote_set._type = emote_set._type || 1;
|
||||||
|
|
||||||
emote_set = this.load_set(id, emote_set);
|
emote_set = this.load_set(set_id, emote_set);
|
||||||
} else
|
} else
|
||||||
emote_set = this.emote_sets[exact_id];
|
emote_set = this.emote_sets[set_id];
|
||||||
|
|
||||||
if ( ! emote_set )
|
if ( ! emote_set )
|
||||||
throw new Error("Invalid set ID");
|
throw new Error("Invalid set ID");
|
||||||
|
@ -386,9 +393,9 @@ API.prototype.register_room_set = function(room_id, id, emote_set) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
API.prototype.unregister_room_set = function(room_id, id) {
|
API.prototype.unregister_room_set = function(room_id, set_id) {
|
||||||
var exact_id = this.id + '-' + id,
|
var exact_id = this.id + '-' + set_id,
|
||||||
emote_set = this.emote_sets[exact_id],
|
emote_set = this.emote_sets[set_id],
|
||||||
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
room = this.ffz.rooms && this.ffz.rooms[room_id];
|
||||||
|
|
||||||
if ( ! emote_set || ! room )
|
if ( ! emote_set || ! room )
|
||||||
|
@ -483,8 +490,8 @@ API.prototype.user_add_set = function(username, set_id) {
|
||||||
|
|
||||||
exact_id = this.id + '-' + set_id;
|
exact_id = this.id + '-' + set_id;
|
||||||
|
|
||||||
if ( emote_sets.indexOf(exact_id) === -1 )
|
if ( emote_sets.indexOf(set_id) === -1 )
|
||||||
emote_sets.push(exact_id);
|
emote_sets.push(set_id);
|
||||||
|
|
||||||
if ( ffz_sets.indexOf(exact_id) === -1 )
|
if ( ffz_sets.indexOf(exact_id) === -1 )
|
||||||
ffz_sets.push(exact_id);
|
ffz_sets.push(exact_id);
|
||||||
|
@ -505,7 +512,7 @@ API.prototype.user_remove_set = function(username, set_id) {
|
||||||
|
|
||||||
exact_id = this.id + '-' + set_id;
|
exact_id = this.id + '-' + set_id;
|
||||||
|
|
||||||
var ind = emote_sets ? emote_sets.indexOf(exact_id) : -1;
|
var ind = emote_sets ? emote_sets.indexOf(set_id) : -1;
|
||||||
if ( ind !== -1 )
|
if ( ind !== -1 )
|
||||||
emote_sets.splice(ind, 1);
|
emote_sets.splice(ind, 1);
|
||||||
|
|
||||||
|
@ -550,7 +557,7 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
|
||||||
try {
|
try {
|
||||||
specific_func(room_id, callback);
|
specific_func(room_id, callback);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log("Error in On-Room Callback: " + err);
|
this.error("Error in On-Room Callback", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -559,7 +566,7 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
|
||||||
try {
|
try {
|
||||||
cb(room_id, callback);
|
cb(room_id, callback);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log("Error in On-Room Callback: " + err);
|
this.error("Error in On-Room Callback", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,11 +575,16 @@ API.prototype._room_callbacks = function(room_id, room, specific_func) {
|
||||||
|
|
||||||
API.prototype.register_on_room_callback = function(callback, dont_iterate) {
|
API.prototype.register_on_room_callback = function(callback, dont_iterate) {
|
||||||
this.on_room_callbacks.push(callback);
|
this.on_room_callbacks.push(callback);
|
||||||
|
var register_callback = this.register_room_set.bind(this, room_id);
|
||||||
|
|
||||||
// Call this for all current rooms.
|
// Call this for all current rooms.
|
||||||
if ( ! dont_iterate && this.ffz.rooms ) {
|
if ( ! dont_iterate && this.ffz.rooms ) {
|
||||||
for(var room_id in this.ffz.rooms)
|
for(var room_id in this.ffz.rooms)
|
||||||
this._room_callbacks(room_id, this.ffz.rooms[room_id], callback);
|
try {
|
||||||
|
callback(room_id, register_callback);
|
||||||
|
} catch(err) {
|
||||||
|
this.error("Error in On-Room Callback", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
cl.remove("ffz-portrait");
|
cl.remove("ffz-portrait");
|
||||||
cl.remove("ffz-minimal-channel-title");
|
cl.remove("ffz-minimal-channel-title");
|
||||||
cl.remove("ffz-flip-dashboard");
|
cl.remove("ffz-flip-dashboard");
|
||||||
|
cl.remove('ffz-minimal-channel-bar');
|
||||||
|
cl.remove('ffz-channel-bar-bottom');
|
||||||
|
cl.remove('ffz-channel-title-top');
|
||||||
|
|
||||||
// Remove Following Count
|
// Remove Following Count
|
||||||
if ( this.settings.following_count ) {
|
if ( this.settings.following_count ) {
|
||||||
|
@ -270,8 +273,6 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.log("BTTV Emotes", output);
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/main.js
20
src/main.js
|
@ -34,7 +34,7 @@ FFZ.msg_commands = {};
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
var VER = FFZ.version_info = {
|
var VER = FFZ.version_info = {
|
||||||
major: 3, minor: 5, revision: 283,
|
major: 3, minor: 5, revision: 302,
|
||||||
toString: function() {
|
toString: function() {
|
||||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||||
}
|
}
|
||||||
|
@ -249,7 +249,7 @@ FFZ.prototype.initialize = function(increment, delay) {
|
||||||
return this.init_clips(delay);
|
return this.init_clips(delay);
|
||||||
|
|
||||||
// Check for special non-ember pages.
|
// Check for special non-ember pages.
|
||||||
if ( /^\/(?:$|search$|team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) )
|
if ( /^\/(?:team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) )
|
||||||
return this.init_normal(delay);
|
return this.init_normal(delay);
|
||||||
|
|
||||||
// Check for the dashboard.
|
// Check for the dashboard.
|
||||||
|
@ -410,6 +410,7 @@ FFZ.prototype.init_dashboard = function(delay) {
|
||||||
// Set up the FFZ message passer.
|
// Set up the FFZ message passer.
|
||||||
this.setup_message_event();
|
this.setup_message_event();
|
||||||
|
|
||||||
|
this.cache_command_aliases();
|
||||||
this.fix_tooltips();
|
this.fix_tooltips();
|
||||||
this.find_bttv(10);
|
this.find_bttv(10);
|
||||||
|
|
||||||
|
@ -433,12 +434,6 @@ FFZ.prototype.init_ember = function(delay) {
|
||||||
} catch(err) { this.embed_in_dash = false; }
|
} catch(err) { this.embed_in_dash = false; }
|
||||||
|
|
||||||
|
|
||||||
// Make an alias so they STOP RENAMING THIS ON ME
|
|
||||||
var Settings = FFZ.utils.ember_lookup('controller:settings');
|
|
||||||
if ( Settings && Settings.get('settings') === undefined )
|
|
||||||
Settings.reopen({settings: Ember.computed.alias('model')});
|
|
||||||
|
|
||||||
|
|
||||||
// Settings are important.
|
// Settings are important.
|
||||||
this.load_settings();
|
this.load_settings();
|
||||||
|
|
||||||
|
@ -448,10 +443,18 @@ FFZ.prototype.init_ember = function(delay) {
|
||||||
var f = this;
|
var f = this;
|
||||||
if ( Ember.RSVP && Ember.RSVP.on )
|
if ( Ember.RSVP && Ember.RSVP.on )
|
||||||
Ember.RSVP.on('error', function(error) {
|
Ember.RSVP.on('error', function(error) {
|
||||||
|
// We want to ignore errors that are just 4xx HTTP responses.
|
||||||
|
if ( error && error.responseJSON && typeof error.responseJSON.status === "number" && error.responseJSON.status >= 400 )
|
||||||
|
return;
|
||||||
|
|
||||||
f.error("There was an error within an Ember RSVP.", error);
|
f.error("There was an error within an Ember RSVP.", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
Ember.onerror = function(error) {
|
Ember.onerror = function(error) {
|
||||||
|
// We want to ignore errors that are just 4xx HTTP responses.
|
||||||
|
if ( error && error.responseJSON && typeof error.responseJSON.status === "number" && error.responseJSON.status >= 400 )
|
||||||
|
return;
|
||||||
|
|
||||||
f.error("There was an unknown error within Ember.", error);
|
f.error("There was an unknown error within Ember.", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,6 +508,7 @@ FFZ.prototype.init_ember = function(delay) {
|
||||||
// Do all Ember modification before this point.
|
// Do all Ember modification before this point.
|
||||||
this.finalize_ember_wrapper();
|
this.finalize_ember_wrapper();
|
||||||
|
|
||||||
|
this.cache_command_aliases();
|
||||||
this.fix_tooltips();
|
this.fix_tooltips();
|
||||||
this.connect_extra_chat();
|
this.connect_extra_chat();
|
||||||
|
|
||||||
|
|
|
@ -815,8 +815,8 @@ FFZ.prototype._setting_get = function(key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FFZ.prototype._setting_get_twitch = function(key) {
|
FFZ.prototype._setting_get_twitch = function(key) {
|
||||||
var Settings = utils.ember_lookup('controller:settings');
|
var Settings = utils.ember_settings();
|
||||||
return Settings && Settings.get('settings.' + key);
|
return Settings && Settings.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -192,18 +192,18 @@ FFZ.prototype.ws_create = function() {
|
||||||
if ( room.important ) {
|
if ( room.important ) {
|
||||||
f.ws_sub("room." + room_id);
|
f.ws_sub("room." + room_id);
|
||||||
|
|
||||||
if ( room.needs_history ) {
|
/*if ( room.needs_history ) {
|
||||||
room.needs_history = false;
|
room.needs_history = false;
|
||||||
if ( ! f.has_bttv && f.settings.chat_history )
|
if ( ! f.has_bttv && f.settings.chat_history )
|
||||||
f.ws_send("chat_history", [room_id,25], f._load_history.bind(f, room_id));
|
f.ws_send("chat_history", [room_id,25], f._load_history.bind(f, room_id));
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the channel(s).
|
// Send the channel(s).
|
||||||
if ( f._cindex ) {
|
if ( f._cindex ) {
|
||||||
var channel_id = f._cindex.get('controller.model.id'),
|
var channel_id = f._cindex.get('channel.id'),
|
||||||
hosted_id = f._cindex.get('controller.hostModeTarget.id');
|
hosted_id = f._cindex.get('channel.hostModeTarget.id');
|
||||||
|
|
||||||
if ( channel_id )
|
if ( channel_id )
|
||||||
f.ws_sub("channel." + channel_id);
|
f.ws_sub("channel." + channel_id);
|
||||||
|
@ -463,20 +463,18 @@ FFZ.ws_commands.do_authorize = function(data) {
|
||||||
if ( ! this.rooms.hasOwnProperty(room_id) )
|
if ( ! this.rooms.hasOwnProperty(room_id) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var r = this.rooms[room_id];
|
var r = this.rooms[room_id],
|
||||||
|
c = r && r.room && r.room.tmiRoom && r.room.tmiRoom._getConnection();
|
||||||
|
|
||||||
if ( r && r.room && !r.room.get('roomProperties.eventchat') && !r.room.get('isGroupRoom') && r.room.tmiRoom ) {
|
|
||||||
var c = r.room.tmiRoom._getConnection();
|
|
||||||
if ( c.isConnected ) {
|
if ( c.isConnected ) {
|
||||||
conn = c;
|
conn = c;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ( conn )
|
if ( conn )
|
||||||
conn._send("PRIVMSG #frankerfacezauthorizer :AUTH " + data);
|
conn._send("PRIVMSG #frankerfacezauthorizer :AUTH " + data);
|
||||||
else
|
else
|
||||||
// Try again shortly.
|
// Try again shortly.
|
||||||
setTimeout(FFZ.ws_commands.do_authorize.bind(this, data), 5000);
|
setTimeout(FFZ.ws_commands.do_authorize.bind(this, data), 1000);
|
||||||
}
|
}
|
|
@ -728,6 +728,10 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
||||||
// We have a mention!
|
// We have a mention!
|
||||||
msgObject.ffz_has_mention = true;
|
msgObject.ffz_has_mention = true;
|
||||||
|
|
||||||
|
// If it's a historical message we don't want to update any other UI.
|
||||||
|
if ( msg.tags && msg.tags.historical )
|
||||||
|
continue;
|
||||||
|
|
||||||
// If we have chat tabs/rows, update the status.
|
// If we have chat tabs/rows, update the status.
|
||||||
if ( room_id && ! this.has_bttv && this._chatv ) {
|
if ( room_id && ! this.has_bttv && this._chatv ) {
|
||||||
var room = this.rooms[room_id] && this.rooms[room_id].room;
|
var room = this.rooms[room_id] && this.rooms[room_id].room;
|
||||||
|
@ -1227,7 +1231,7 @@ FFZ._words_to_regex = function(list) {
|
||||||
if ( ! list[i] )
|
if ( ! list[i] )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
reg += (reg ? "|" : "") + reg_escape(list[i]);
|
reg += (reg ? "|" : "") + (list[i].substr(0,6) === "regex:" ? list[i].substr(6) : reg_escape(list[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + constants.SEPARATORS + ")(" + reg + ")(?=$|" + constants.SEPARATORS + ")", "ig");
|
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + constants.SEPARATORS + ")(" + reg + ")(?=$|" + constants.SEPARATORS + ")", "ig");
|
||||||
|
|
|
@ -89,7 +89,7 @@ FFZ.basic_settings.keywords = {
|
||||||
help: "Set additional keywords that will be highlighted in chat.",
|
help: "Set additional keywords that will be highlighted in chat.",
|
||||||
|
|
||||||
method: function() {
|
method: function() {
|
||||||
FFZ.settings_info.keywords.method.call(this);
|
FFZ.settings_info.keywords.method.call(this, null, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ FFZ.basic_settings.banned_words = {
|
||||||
help: "Set a list of words that will be removed from chat messages, locally.",
|
help: "Set a list of words that will be removed from chat messages, locally.",
|
||||||
|
|
||||||
method: function() {
|
method: function() {
|
||||||
FFZ.settings_info.banned_words.method.call(this);
|
FFZ.settings_info.banned_words.method.call(this, null, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,15 +137,14 @@ FFZ.settings_info.dark_twitch = {
|
||||||
|
|
||||||
(this.is_clips ? document.querySelector('html') : document.body).classList.toggle("ffz-dark", val);
|
(this.is_clips ? document.querySelector('html') : document.body).classList.toggle("ffz-dark", val);
|
||||||
|
|
||||||
var Settings = utils.ember_lookup('controller:settings'),
|
var Settings = utils.ember_settings();
|
||||||
settings = Settings && Settings.get('settings');
|
|
||||||
|
|
||||||
if ( val ) {
|
if ( val ) {
|
||||||
this._load_dark_css();
|
this._load_dark_css();
|
||||||
settings && this.settings.set('twitch_chat_dark', settings.get('darkMode'));
|
Settings && this.settings.set('twitch_chat_dark', Settings.get('darkMode'));
|
||||||
settings && settings.set('darkMode', true);
|
Settings && Settings.set('darkMode', true);
|
||||||
} else
|
} else
|
||||||
settings && settings.set('darkMode', this.settings.twitch_chat_dark);
|
Settings && Settings.set('darkMode', this.settings.twitch_chat_dark);
|
||||||
|
|
||||||
// Try coloring chat replay
|
// Try coloring chat replay
|
||||||
window.jQuery && jQuery('.chatReplay').toggleClass('dark', val || false);
|
window.jQuery && jQuery('.chatReplay').toggleClass('dark', val || false);
|
||||||
|
@ -203,10 +202,10 @@ FFZ.prototype.setup_dark = function() {
|
||||||
if ( ! this.settings.dark_twitch )
|
if ( ! this.settings.dark_twitch )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var Settings = utils.ember_lookup('controller:settings');
|
var Settings = utils.ember_settings();
|
||||||
if ( Settings ) {
|
if ( Settings ) {
|
||||||
try {
|
try {
|
||||||
Settings.set('settings.darkMode', true);
|
Settings.set('darkMode', true);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.error("Unable to set the darkMode setting because it isn't named what we expect. WTF?");
|
this.error("Unable to set the darkMode setting because it isn't named what we expect. WTF?");
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,8 +76,8 @@ FFZ.ffz_commands.following = function(room, args) {
|
||||||
|
|
||||||
FFZ.ws_on_close.push(function() {
|
FFZ.ws_on_close.push(function() {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
var controller = utils.ember_lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('content.id'),
|
current_id = controller && controller.get('channelModel.id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('channelModel.hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
this.follow_sets = {};
|
this.follow_sets = {};
|
||||||
|
@ -112,8 +112,8 @@ FFZ.ws_on_close.push(function() {
|
||||||
|
|
||||||
FFZ.ws_commands.follow_buttons = function(data) {
|
FFZ.ws_commands.follow_buttons = function(data) {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
var controller = utils.ember_lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('content.id'),
|
current_id = controller && controller.get('channelModel.id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('channelModel.hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
this.follow_data = this.follow_data || {};
|
this.follow_data = this.follow_data || {};
|
||||||
|
@ -131,8 +131,8 @@ FFZ.ws_commands.follow_buttons = function(data) {
|
||||||
|
|
||||||
FFZ.ws_commands.follow_sets = function(data) {
|
FFZ.ws_commands.follow_sets = function(data) {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
var controller = utils.ember_lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('content.id'),
|
current_id = controller && controller.get('channelModel.id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('channelModel.hostModeTarget.id'),
|
||||||
need_update = false,
|
need_update = false,
|
||||||
f = this;
|
f = this;
|
||||||
|
|
||||||
|
@ -187,13 +187,12 @@ FFZ.ws_commands.follow_sets = function(data) {
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
FFZ.prototype.rebuild_following_ui = function() {
|
FFZ.prototype.rebuild_following_ui = function() {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
|
||||||
channel_id = controller && controller.get('content.id'),
|
|
||||||
hosted_id = controller && controller.get('hostModeTarget.id');
|
|
||||||
|
|
||||||
if ( ! this._cindex )
|
if ( ! this._cindex )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var channel_id = this._cindex.get('channel.id'),
|
||||||
|
hosted_id = this._cindex.get('channel.hostModeTarget.id');
|
||||||
|
|
||||||
if ( channel_id ) {
|
if ( channel_id ) {
|
||||||
var data = this.follow_data && this.follow_data[channel_id],
|
var data = this.follow_data && this.follow_data[channel_id],
|
||||||
|
|
||||||
|
|
|
@ -45,42 +45,20 @@ FFZ.prototype.setup_menu = function() {
|
||||||
|
|
||||||
this.log("Hooking the Ember Chat Settings view.");
|
this.log("Hooking the Ember Chat Settings view.");
|
||||||
|
|
||||||
var Settings = utils.ember_resolve('view:settings'),
|
var Settings = utils.ember_resolve('component:chat/chat-settings-menu'),
|
||||||
Layout = utils.ember_lookup('service:layout'),
|
Layout = utils.ember_lookup('service:layout'),
|
||||||
f = this;
|
f = this;
|
||||||
|
|
||||||
if ( ! Settings )
|
if ( ! Settings )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Settings.reopen({
|
utils.ember_reopen_view(Settings, {
|
||||||
didInsertElement: function() {
|
ffz_init: function() {
|
||||||
this._super();
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.ffzInit();
|
|
||||||
} catch(err) {
|
|
||||||
f.error("ChatSettings didInsertElement: " + err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
willClearRender: function() {
|
|
||||||
try {
|
|
||||||
this.ffzTeardown();
|
|
||||||
} catch(err) {
|
|
||||||
f.error("ChatSettings willClearRender: " + err);
|
|
||||||
}
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
|
|
||||||
ffzInit: function() {
|
|
||||||
var view = this,
|
var view = this,
|
||||||
el = this.get('element'),
|
el = this.get('element');
|
||||||
menu = el && el.querySelector('.dropmenu');
|
|
||||||
|
|
||||||
if ( ! menu )
|
var container = utils.createElement('div', ''),
|
||||||
return;
|
header = utils.createElement('div', 'list-header', 'FrankerFaceZ'),
|
||||||
|
|
||||||
var header = utils.createElement('div', 'list-header', 'FrankerFaceZ'),
|
|
||||||
content = utils.createElement('div', 'chat-menu-content'),
|
content = utils.createElement('div', 'chat-menu-content'),
|
||||||
p, cb, a;
|
p, cb, a;
|
||||||
|
|
||||||
|
@ -125,25 +103,35 @@ FFZ.prototype.setup_menu = function() {
|
||||||
content.appendChild(p);
|
content.appendChild(p);
|
||||||
|
|
||||||
a.addEventListener('click', function(e) {
|
a.addEventListener('click', function(e) {
|
||||||
view.set('controller.settings.hidden', true);
|
view.set('isHidden', true);
|
||||||
f._last_page = 'settings';
|
f._last_page = 'settings';
|
||||||
f.build_ui_popup(f._chatv);
|
f.build_ui_popup(f._chatv);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
menu.appendChild(header);
|
container.appendChild(header);
|
||||||
menu.appendChild(content);
|
container.appendChild(content);
|
||||||
|
|
||||||
|
container.classList.toggle('hidden', this.get('showDisplaySettings'));
|
||||||
|
el.appendChild(container);
|
||||||
|
this.ffz_menu = container;
|
||||||
|
|
||||||
// Maximum Height
|
// Maximum Height
|
||||||
var e = el.querySelector('.chat-settings');
|
if ( Layout && el )
|
||||||
if ( Layout && e )
|
el.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
|
||||||
e.style.maxHeight = (Layout.get('windowHeight') - 90) + 'px';
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzTeardown: function() {
|
ffz_update_visibility: function() {
|
||||||
// Nothing~!
|
if ( this.ffz_menu )
|
||||||
|
this.ffz_menu.classList.toggle('hidden', this.get('showDisplaySettings'));
|
||||||
|
}.observes('showDisplaySettings'),
|
||||||
|
|
||||||
|
ffz_destroy: function() {
|
||||||
|
if ( this.ffz_menu ) {
|
||||||
|
this.ffz_menu.parentElement.removeChild(this.ffz_menu);
|
||||||
|
this.ffz_menu = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -648,6 +636,9 @@ FFZ.menu_pages.channel = {
|
||||||
var set = this.emote_sets[extra_sets[i]],
|
var set = this.emote_sets[extra_sets[i]],
|
||||||
name = set ? (set.hasOwnProperty('source_ext') ? "" : "Featured ") + set.title : "Featured Channel";
|
name = set ? (set.hasOwnProperty('source_ext') ? "" : "Featured ") + set.title : "Featured Channel";
|
||||||
|
|
||||||
|
if ( ! set || ! set.count || set.hidden )
|
||||||
|
continue;
|
||||||
|
|
||||||
this._emotes_for_sets(inner, view, [extra_sets[i]], name, set.icon || "//cdn.frankerfacez.com/script/devicon.png", set.source || "FrankerFaceZ");
|
this._emotes_for_sets(inner, view, [extra_sets[i]], name, set.icon || "//cdn.frankerfacez.com/script/devicon.png", set.source || "FrankerFaceZ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ FFZ.menu_pages.myemotes = {
|
||||||
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
|
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( ! set || ! set.count || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
|
if ( ! set || ! set.count || set.hidden || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var menu = FFZ.menu_pages.myemotes.draw_ffz_set.call(this, view, set, favorites_only);
|
var menu = FFZ.menu_pages.myemotes.draw_ffz_set.call(this, view, set, favorites_only);
|
||||||
|
|
|
@ -36,8 +36,8 @@ FFZ.settings_info.srl_races = {
|
||||||
|
|
||||||
FFZ.ws_on_close.push(function() {
|
FFZ.ws_on_close.push(function() {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
var controller = utils.ember_lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('content.id'),
|
current_id = controller && controller.get('channelModel.id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('channelModel.hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
if ( ! controller )
|
if ( ! controller )
|
||||||
|
@ -56,8 +56,8 @@ FFZ.ws_on_close.push(function() {
|
||||||
|
|
||||||
FFZ.ws_commands.srl_race = function(data) {
|
FFZ.ws_commands.srl_race = function(data) {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
var controller = utils.ember_lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('content.id'),
|
current_id = controller && controller.get('channelModel.id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('channelModel.hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
this.srl_races = this.srl_races || {};
|
this.srl_races = this.srl_races || {};
|
||||||
|
@ -91,13 +91,12 @@ FFZ.ws_commands.srl_race = function(data) {
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
FFZ.prototype.rebuild_race_ui = function() {
|
FFZ.prototype.rebuild_race_ui = function() {
|
||||||
var controller = utils.ember_lookup('controller:channel'),
|
|
||||||
channel_id = controller && controller.get('content.id'),
|
|
||||||
hosted_id = controller && controller.get('hostModeTarget.id');
|
|
||||||
|
|
||||||
if ( ! this._cindex )
|
if ( ! this._cindex )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var channel_id = this._cindex.get('channel.id'),
|
||||||
|
hosted_id = this._cindex.get('channel.hostModeTarget.id');
|
||||||
|
|
||||||
if ( channel_id ) {
|
if ( channel_id ) {
|
||||||
var race = this.srl_races && this.srl_races[channel_id],
|
var race = this.srl_races && this.srl_races[channel_id],
|
||||||
|
|
||||||
|
|
84
src/utils.js
84
src/utils.js
|
@ -267,7 +267,20 @@ var createElement = function(tag, className, content) {
|
||||||
FrankerFaceZ.get().error("There was an error looking up an Ember instance: " + thing, err);
|
FrankerFaceZ.get().error("There was an error looking up an Ember instance: " + thing, err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
|
ember_resolve = function(thing) {
|
||||||
|
if ( ! window.App )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance__.registry.resolve )
|
||||||
|
return App.__deprecatedInstance__.registry.resolve(thing);
|
||||||
|
if ( App.__container__ && App.__container__.resolve )
|
||||||
|
return App.__container__.resolve(thing);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
CMD_VAR_REGEX = /{(\d+(?:\$(?:\d+)?)?|id|msg_id|message_id|(?:user|room)(?:_id|_name|_display_name)?)}/g;
|
||||||
|
|
||||||
|
|
||||||
module.exports = FFZ.utils = {
|
module.exports = FFZ.utils = {
|
||||||
|
@ -277,15 +290,10 @@ module.exports = FFZ.utils = {
|
||||||
},
|
},
|
||||||
|
|
||||||
ember_lookup: ember_lookup,
|
ember_lookup: ember_lookup,
|
||||||
|
ember_resolve: ember_resolve,
|
||||||
ember_resolve: function(thing) {
|
ember_settings: function() {
|
||||||
if ( ! window.App )
|
var settings = ember_resolve('model:settings');
|
||||||
return;
|
return settings && settings.findOne();
|
||||||
|
|
||||||
if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance__.registry.resolve )
|
|
||||||
return App.__deprecatedInstance__.registry.resolve(thing);
|
|
||||||
if ( App.__container__ && App.__container__.resolve )
|
|
||||||
return App.__container__.resolve(thing);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ember_reopen_view: function(component, data) {
|
ember_reopen_view: function(component, data) {
|
||||||
|
@ -332,6 +340,62 @@ module.exports = FFZ.utils = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
find_parent: function(el, klass) {
|
||||||
|
while (el && el.parentNode) {
|
||||||
|
el = el.parentNode;
|
||||||
|
if ( el.classList.contains(klass) )
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
CMD_VAR_REGEX: CMD_VAR_REGEX,
|
||||||
|
|
||||||
|
replace_cmd_variables: function(command, user, room, message, args) {
|
||||||
|
user = user || {};
|
||||||
|
room = room || {};
|
||||||
|
message = message || {};
|
||||||
|
message.tags = message.tags || {};
|
||||||
|
|
||||||
|
var msg_id = message.tags.id,
|
||||||
|
replacements = {
|
||||||
|
user: user.name,
|
||||||
|
user_name: user.name,
|
||||||
|
user_display_name: user.display_name || message.tags['display-name'],
|
||||||
|
user_id: user._id || message.tags['user-id'],
|
||||||
|
|
||||||
|
room: room.id,
|
||||||
|
room_name: room.id,
|
||||||
|
room_display_name: room.get && (room.get('tmiRoom.displayName') || room.get('channel.displayName')),
|
||||||
|
room_id: room.get && room.get('roomProperties._id') || message.tags['room-id'],
|
||||||
|
|
||||||
|
id: msg_id,
|
||||||
|
message_id: msg_id,
|
||||||
|
msg_id: msg_id
|
||||||
|
};
|
||||||
|
|
||||||
|
CMD_VAR_REGEX.lastIndex = 0;
|
||||||
|
return command.replace(CMD_VAR_REGEX, function(match, variable) {
|
||||||
|
if ( replacements[variable] )
|
||||||
|
return replacements[variable];
|
||||||
|
|
||||||
|
if ( args ) {
|
||||||
|
var match = /(\d+)(?:(\$)(\d+)?)?/.exec(variable);
|
||||||
|
if ( match ) {
|
||||||
|
var num = parseInt(match[1]),
|
||||||
|
second_num = match[3] ? parseInt(match[3]) : undefined;
|
||||||
|
|
||||||
|
return match[2] === '$' ? args.slice(num, second_num).join(" ") : args[num];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '{' + variable + '}';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
show_modal: show_modal,
|
show_modal: show_modal,
|
||||||
confirm: function(title, description, callback) {
|
confirm: function(title, description, callback) {
|
||||||
var contents = createElement('div', 'text-content'),
|
var contents = createElement('div', 'text-content'),
|
||||||
|
|
138
style.css
138
style.css
|
@ -18,6 +18,10 @@ body > div.tipsy .tipsy-arrow { opacity: 0.8; }
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-minimal-channel-title .cn-metabar__title:not(:hover) .card__info,
|
||||||
|
.ffz-minimal-channel-bar .cn-bar-spacer,
|
||||||
|
.ffz-channel-bar-bottom .cn-bar-spacer,
|
||||||
|
.ffz-channel-bar-bottom .cn-cover-wrap,
|
||||||
.app-main .ad_leader:empty,
|
.app-main .ad_leader:empty,
|
||||||
body:not(.ffz-show-bits-tags) .bits-tag--container,
|
body:not(.ffz-show-bits-tags) .bits-tag--container,
|
||||||
.ffz-hide-friends nav .friend-list,
|
.ffz-hide-friends nav .friend-list,
|
||||||
|
@ -28,12 +32,37 @@ body:not(.ffz-show-bits-tags) .bits-tag--container,
|
||||||
.ffz-hide-promoted-games .promotedGames,
|
.ffz-hide-promoted-games .promotedGames,
|
||||||
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
|
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
|
||||||
.ffz-hide-view-count .stat.twitch-channel-views,
|
.ffz-hide-view-count .stat.twitch-channel-views,
|
||||||
|
.ffz-hide-view-count .cn-metabar__viewcount,
|
||||||
.ffz-minimal-chat-input .chat-interface .emoticon-selector-toggle,
|
.ffz-minimal-chat-input .chat-interface .emoticon-selector-toggle,
|
||||||
.ffz-menu-replace .chat-interface .ember-emoticon-selector,
|
.ffz-menu-replace .chat-interface .ember-emoticon-selector,
|
||||||
.ffz-menu-replace .chat-interface .emoticon-selector-toggle {
|
.ffz-menu-replace .chat-interface .emoticon-selector-toggle {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-channel-bar-bottom #channel {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-bar-bottom .notification-controls .balloon {
|
||||||
|
top: auto;
|
||||||
|
bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-bar-bottom .js-username-hover-tip .balloon {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-bar-bottom .cn-bar {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: inset 0 1px 0 #e5e3e8,0 -1px -1px rgba(0,0,0,.065);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark.ffz-channel-bar-bottom .cn-bar {
|
||||||
|
box-shadow: inset 0 1px 0 #161616, -1px -1px rgba(255,255,255,0.065);
|
||||||
|
}
|
||||||
|
|
||||||
body:not(.ffz-show-bits-tags) .ember-chat .chat-messages.bits-tags__offset {
|
body:not(.ffz-show-bits-tags) .ember-chat .chat-messages.bits-tags__offset {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
@ -223,31 +252,18 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
|
||||||
|
|
||||||
/* Minimal Channel Title */
|
/* Minimal Channel Title */
|
||||||
|
|
||||||
.ffz-minimal-channel-title #broadcast-meta {
|
.ffz-minimal-channel-title .cn-metabar__title:hover {
|
||||||
height: 25px;
|
background-color: #fff;
|
||||||
margin: 20px 0;
|
padding: 1rem;
|
||||||
|
margin-left: -1rem;
|
||||||
}
|
}
|
||||||
|
.ffz-dark.ffz-minimal-channel-title .cn-metabar__title:hover { background-color: #101010 }
|
||||||
|
|
||||||
.ffz-minimal-channel-title #channel #broadcast-meta .profile-link .profile-photo img {
|
.ffz-minimal-channel-title .cn-metabar__title:not(:hover) .card__img--boxart {
|
||||||
width: 25px; height: 25px;
|
height: 20px !important;
|
||||||
|
width: 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-minimal-channel-title #channel #broadcast-meta .info {
|
|
||||||
padding-top: 25px;
|
|
||||||
padding-left: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ffz-minimal-channel-title #channel #broadcast-meta .info .title {
|
|
||||||
padding-top: 0;
|
|
||||||
margin: 0;
|
|
||||||
left: calc(25px + 1.5rem);
|
|
||||||
height: 25px;
|
|
||||||
min-height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ffz-minimal-channel-title #broadcast-meta .profile-photo .goto,
|
|
||||||
.ffz-minimal-channel-title #broadcast-meta .channel { display: none }
|
|
||||||
|
|
||||||
|
|
||||||
/* Theater Mode hover bar */
|
/* Theater Mode hover bar */
|
||||||
|
|
||||||
|
@ -1404,6 +1420,7 @@ img.channel_background[src="null"] { display: none; }
|
||||||
background-color: #232329;
|
background-color: #232329;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-no-blue .cn-hosting,
|
||||||
.ffz-no-blue .dark .ember-chat .moderation-card .interface,
|
.ffz-no-blue .dark .ember-chat .moderation-card .interface,
|
||||||
.ffz-no-blue .force-dark .ember-chat .moderation-card .interface,
|
.ffz-no-blue .force-dark .ember-chat .moderation-card .interface,
|
||||||
.ffz-no-blue .theatre .ember-chat .moderation-card .interface {
|
.ffz-no-blue .theatre .ember-chat .moderation-card .interface {
|
||||||
|
@ -1428,6 +1445,11 @@ img.channel_background[src="null"] { display: none; }
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: -1px !important;
|
margin-top: -1px !important;
|
||||||
color: #888 !important;
|
color: #888 !important;
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-icons .mod-icon img {
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1550,6 +1572,8 @@ body:not(.ffz-bttv) .chat-container:not(.chatReplay) .more-messages-indicator {
|
||||||
|
|
||||||
/* Menu About Page */
|
/* Menu About Page */
|
||||||
|
|
||||||
|
#ffz-chat-menu .center { text-align: center }
|
||||||
|
|
||||||
.ffz-about-table {
|
.ffz-about-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -2039,8 +2063,11 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz.room-state.faded { opacity: 0.5 }
|
||||||
|
|
||||||
.ffz.room-state.truncated span { font-size: 8px; }
|
.ffz.room-state.truncated span { font-size: 8px; }
|
||||||
|
|
||||||
.button.primary.ffz-waiting:not(:hover) {
|
.button.primary.ffz-waiting:not(:hover) {
|
||||||
|
@ -2082,6 +2109,14 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-sidebar-swap #main_col .cn-bar-fixed {
|
||||||
|
right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-sidebar-swap #left_col.open ~ #main_col .cn-bar-fixed {
|
||||||
|
right: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
.ffz-sidebar-swap #left_col {
|
.ffz-sidebar-swap #left_col {
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -3235,3 +3270,64 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
border-color: rgba(255,255,255,0.2);
|
border-color: rgba(255,255,255,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-mod-balloon {
|
||||||
|
display: block;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-mod-balloon .ffz-title {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.cn-metabar__ffz svg { fill: #898395; }
|
||||||
|
.cn-metabar__ffz {
|
||||||
|
cursor: default;
|
||||||
|
color: #706a7c;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
.cn-metabar__ffz figure {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-title-top .cn-metabar__more { width: 100% }
|
||||||
|
.ffz-channel-title-top #channel {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-minimal-channel-title.ffz-channel-title-top #channel {
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-title-top .cn-metabar__title {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-title-top:not(.ffz-channel-bar-bottom) #channel.ffz-bar-fixed .cn-metabar__title {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > div { order: 999; }
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > span:last-of-type { flex-grow: 1 }
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > span:last-of-type > div { float: left }
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > div:last-of-type { margin-right: 0 !important }
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > div + button:first-of-type,
|
||||||
|
.ffz-channel-title-top .cn-metabar__more > div + span:first-of-type button.mg-l-1 { margin-left: 0 !important }
|
||||||
|
.ffz-channel-title-top .cn-metabar__viewcount { flex-grow: 0 !important; }
|
||||||
|
|
||||||
|
.ffz-minimal-channel-bar .cn-bar-fixed {
|
||||||
|
top: -40px;
|
||||||
|
transition: top ease-in-out 75ms, bottom ease-in-out 75ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-minimal-channel-bar .cn-bar-fixed:hover { top: 0 }
|
||||||
|
.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar-fixed { top: auto; bottom: -40px }
|
||||||
|
.ffz-minimal-channel-bar.ffz-channel-bar-bottom .cn-bar-fixed:hover { bottom: 0 }
|
||||||
|
|
||||||
|
.ffz-minimal-channel-title .cn-metabar,
|
||||||
|
.ffz-channel-title-top .cn-metabar {
|
||||||
|
min-height: 70px;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue