· 6 years ago · Oct 13, 2019, 03:14 PM
1/*
2The MIT License (MIT)
3
4Copyright (c) 2013-2014 Zimny Lech
5
6Permission is hereby granted, free of charge, to any person obtaining a copy of
7this software and associated documentation files (the "Software"), to deal in
8the Software without restriction, including without limitation the rights to
9use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies off
10the Software, and to permit persons to whom the Software is furnished to do so,
11subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*/
23
24// DESCRIPTION: CyTube Plus - JavaScript and CSS library for CyTube channels enhancements
25// CURRENT VERSION: 4.5
26// DATE OF MODIFICATION: 2014-08-29
27// PROJECT URL: https://github.com/zimny-lech/CyTube-Plus
28
29/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
30
31// INSTALLATION NOTES:
32
33// 0. Choose simple or custom installation (simple one has ready-to-use library with limited functionality).
34// SIMPLE INSTALLATION: use 'https://dl.dropboxusercontent.com/s/2uks4pif8mx2qab/main-ready.js' as default URL
35// go to -> 3.
36// CUSTOM INSTALLATION: go to -> 1.
37
38// 1. Configure this library according to your wishes (see configuration sections below).
39// 2. Save customized library to your file hosting (e.g. Dropbox) or to your own server.
40// WARNING! You must be able to access .js file directly (browser's URL must contain .js extension, not .php etc.).
41// WARNING FOR DROPBOX USERS! Always use 'dl.dropboxusercontent.com' URL instead of 'www.dropbox.com' URL.
42
43// 3. Enter your JS file location into 'External Javascript' field in 'Channel Settings' modal window.
44// 4. Enter library CSS file location into 'External CSS' field in 'Channel Settings' modal window.
45// NOTE: use default 'https://dl.dropboxusercontent.com/s/180y5d6mvlu8kd8/main.css' URL,
46// or copy it to your own location.
47// 5. Congratulations, your have just installed CyTube Plus!
48
49// Need detailed options explanations? Go to https://github.com/zimny-lech/CyTube-Plus/wiki/FAQ
50// Problems? Something doesn't work? Go to https://github.com/zimny-lech/CyTube-Plus/wiki/Troubleshooting
51
52/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
53
54// CONFIGURE BELOW BEFORE INSTALLING IF YOU WANT TO CUSTOMIZE THIS LIBRARY
55
56/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57
58/* ----- STARTING CONFIGURATION - USER INTERFACE (UI) ----- */
59
60// CONFIGURATION NOTES:
61
62// In this section you can immediately enable and disable each option (set '1' to enable, '0' to disable)
63// Every option marked as [&] requires additional configuration (see other sections below)
64// WARNING! apostrophe sign (') in all text/html values must be prepend with "\" sign (e.g. "don\'t")
65
66// FILTERS INSTALLATION: open 'Channel Settings' modal window, go to 'Edit' -> 'Chat Filters',
67// click 'Prepare fonts filters' button, and import
68
69// If you need more explanations, go to https://github.com/zimny-lech/CyTube-Plus/wiki/FAQ
70
71UI_DefaultSynchtube = 0; // default old Synchtube layout (player and playlist on the left)
72UI_Favicon = 1; // [&] channel favicon
73UI_MiniLogo = 1; // [&] small channel logo/avatar in the top navbar
74UI_ChannelName = 1; // [&] channel custom brand name
75UI_HeaderDropMenu = 1; // [&] additional header dropdown menu
76UI_RemoveLayoutMenu = 1; // removing 'Layout' menu from the header
77UI_MOTDAutoLogo = 1; // [&] big channel logo inserted into MOTD
78UI_MOTDTabs = 1; // [&] switchable MOTD tabs application for homepage-like channel header
79UI_MOTDDelete = 1; // deleting previous MOTD after accepting/loading script
80UI_RulesBtn = 1; // [&] button displaying channel rules
81UI_AttentionBar = 0; // [&] imageboard-style attention bar (requires external application)
82UI_ChannelAnnouncement = 1; // [&] additional custom channel announcement
83UI_FullTitleBar = 1; // full-width video title bar
84UI_ProgressBar = 1; // YouTube/Dailymotion progress bar
85 // [ REQUIRE: UI_FullTitleBar enabled ]
86UI_TitleIcon = 1; // [&] full-width title bar icon
87 // [ REQUIRE: UI_FullTitleBar enabled ]
88UI_TitleBarDescription = 1; // [&] custom title bar description (default "Currently Playing:")
89UI_JoinText = 1; // [&] chat message after user joining
90UI_LeaveText = 1; // [&] chat message after user leaving
91UI_UserCommands = 1; // [&] additional commands in the chat window
92UI_UserMarks = 1; // [&] special signs/avatars before every message for defined users
93UI_Squavatars = 1; // automatic squavatars (2-colored square avatars) before every message
94 // [ REQUIRE: UI_UserMarks enabled ]
95UI_UsernameMark = 1; // [&] custom mark after username (default ":")
96UI_MessagesSuffix = 1; // [&] text added to random chat messages
97UI_CustomPingSound = 1; // [&] custom sound for chat notifications
98UI_SoundFilters = 1; // [&] chat sounds played after sending certain words
99UI_ChatSpeak = 1; // text speaking after '!say' and '!mow' commands (english and polish)
100UI_IndependentEmotes = 1; // [&] additional settings-independent emotes
101UI_IndependentFilters = 1; // [&] additional settings-independent filters
102UI_FontsBtn = 1; // button displaying box with clickable chat fonts
103 // [ REQUIRE: INSTALLATION (see above) ]
104UI_UnicodeChars = 1; // [&] additional buttons in the fonts panel with unicode characters
105 // [ REQUIRE: UI_FontsBtn enabled ]
106UI_EmotesBtn = 1; // button displaying box with clickable chat emotes
107UI_GroupEmotes = 1; // [&] emotes panel pagination, display limited number of emotes at one time
108 // [ REQUIRE: UI_EmotesBtn enabled ]
109UI_CommandsBtn = 1; // button displaying modal window with chat commands help
110UI_ModPanel = 1; // [&] panel with messages and help for moderators
111UI_CustomCaptions = 1; // [&] custom captions for add, refresh, voteskip buttons, and welcome text
112UI_PlayerOptions = 1; // [&] additional player options
113UI_TransformationBtns = 1; // player transformation buttons
114UI_ChannelDatabase = 1; // [&] box with embed additional media database
115UI_ChannelGalleries = 1; // [&] box with embed galleries
116UI_DisplayModeSel = 1; // selector with player display modes
117UI_ChannelTheme = 1; // [&] additional default channel theme
118UI_EmbeddingMedia = 1; // [&] possibility to embedding (displaying) images and .webm videos on the chat
119UI_MediaControls = 1; // embedded video preloaded controls
120 // [ REQUIRE: UI_EmbeddingMedia enabled ]
121UI_QuickCommandsBtns = 1; // buttons with '/clear' and '/afk' functions
122UI_VolumeBtns = 1; // additional volume buttons for YouTube player
123UI_EmptyCornerBackground = 1; // [&] random background image for empty playlist row corner
124UI_ExtendedGetURLs = 1; // extended 'Get URLs' function
125UI_DefaultNonTemp = 1; // default unchecking "Add as temporary" checkbox after loading for registered users
126UI_CustomFooter = 1; // [&] custom channel footer
127UI_CustomRightFooter = 0; // [&] right-sided footer box
128UI_UserStatistics = 1; // displaying in the footer user visits number and current online time
129UI_ExternalScript = 0; // [&] additional external script file
130UI_ChannelCache = 1; // caching script emotes, additional media database and default gallery
131
132/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
133
134/* ----- DETAILED BASIC CONFIGURATION ----- */
135
136// NOTES:
137// a) values for 'MOTDAutoLogo_Mode': 1 = first logo; 2 = random logo; 3 = logo rotation; 7 = weekdays logos
138// b) in 'SoundFilters_Array' use .ogg or .wav files, some browsers has problems with embedded .mp3
139// c) in 'ModPanel_Array' item leave empty first (username) field to make a message to all moderators
140// d) in 'EmbeddingMedia_Images' and 'EmbeddingMedia_Videos' you can define acceptable file extensions
141// use CSS syntax: e.g. 'a[href$=""]' defines acceptable end of an URL (file extension)
142// you can also define URL fragments: 'a[href*=""]', or add excluding clause: '.not(\'a[href*=""]\'), etc.
143
144 /* -- single variables -- */
145
146Favicon_URL = 'https://lh4.googleusercontent.com/zrbkowy-uQ4ne3Zkajr0VyrcIN0h_kYnHMqX7yUUjBe7jsnsvPxuXeLJ8UDuhj7N548SQ34v_owLTGqg5r41=w2560-h1290';
147
148MiniLogo_URL = 'https://lh4.googleusercontent.com/zrbkowy-uQ4ne3Zkajr0VyrcIN0h_kYnHMqX7yUUjBe7jsnsvPxuXeLJ8UDuhj7N548SQ34v_owLTGqg5r41=w2560-h1290';
149
150ChannelName_Caption = 'DRGN Cinema';
151
152HeaderDropMenu_Title = 'Synch';
153
154MOTDAutoLogo_Mode = 1;
155
156MOTDAutoLogo_Interval = 20;
157
158RulesBtn_Caption = 'Read Channel Rules';
159
160AttentionBar_URL = '';
161
162ChannelAnnouncement_Title = 'Administration Message';
163
164TitleIcon_URL = 'https://lh4.googleusercontent.com/zrbkowy-uQ4ne3Zkajr0VyrcIN0h_kYnHMqX7yUUjBe7jsnsvPxuXeLJ8UDuhj7N548SQ34v_owLTGqg5r41=w2560-h1290';
165
166TitleBarDescription_Caption = 'Now Playing:';
167
168JoinText_Message = 'joined';
169
170LeaveText_Message = 'left';
171
172UsernameMark_Char = '>';
173
174MessagesSuffix_Text = '~xD';
175
176MessagesSuffix_Percentage = 10;
177
178CustomPingSound_URL = 'https://dl.dropboxusercontent.com/s/0qtsttblgmkewnv/beep.wav';
179
180PlayerHiding_URL = 'https://dl.dropboxusercontent.com/s/xz2o99scw5i7aai/stop.png';
181
182GroupEmotes_Number = 100;
183
184ExternalScript_URL = '';
185
186 /* -- arrays -- */
187
188HeaderDropMenu_Array = [
189['CyTube Servers', ''],
190['Calzoneman CyTube', 'http://cytu.be/'],
191['6irc Synchtube', 'http://synchtube.6irc.net/'],
192['CyTube Stuff', ''],
193['Source code', 'https://github.com/calzoneman/sync'],
194['FAQ', 'https://github.com/calzoneman/sync/wiki/Beginner%27s-Guide-and-FAQ'],
195['IRC', 'http://webchat.6irc.net/?channels=chat,synchtube'],
196['CyTube Plus', 'https://github.com/zimny-lech/CyTube-Plus'],
197];
198
199MOTDAutoLogo_Array = [
200'https://dl.dropboxusercontent.com/s/7mrz85gl29eiiks/logo.png',
201];
202
203MOTDTabs_Array = [
204['Home', 'Welcome to our channel!'],
205['Playlist', 'Playlist tab.<br /><br />We watched this, that and this.'],
206['Schedule', 'Schedule:<br /><br /><ul><li>Monday: ...</li><li>Tuesday: ...</li><li>Wendesday: ...</li><li>Thursday: ...</li><li>Friday: ...</li><li>Saturday: ...</li><li>Sunday: ...</li></ul>'],
207['Contact', 'Contact:<br /><br />Email - ...<br />Skype - ...'],
208];
209
210RandomQuotes_Array = [
211'I like the Pope dancing', 'No quotes today', 'O rly?',
212'People have the right to be stupid. You abuse that privilege', 'Don\'t play stupid with me',
213'Roses are red violets are blue, God made me pretty, what happened to you?',
214'Please don\'t interrupt me while I\'m ignoring you',
215'Are you always this stupid, or are you making a special effort today?',
216'I like you. You remind me of when I was young and stupid.', 'Go and buy me a beer',
217'The door of this channel is always open for you... so feel free to leave!',
218];
219
220AskAnswers_Array = [
221'100% for sure', 'definitely yes', 'yes', 'probably', 'not any chance', 'definitely no',
222'a little chance', 'no', '50/50', 'fairy is tired and will not answer', 'I refuse to answer',
223];
224
225Memes_Array = [
226'>', 'fb', 'omfg', 'u mad',
227];
228
229UserMarks_Array = {
230'ZimnyLech': '<img src="https://dl.dropboxusercontent.com/s/89gv8fvyr49vnev/kobato-icon-small.jpg" height="25">',
231'calzoneman': '♠',
232}
233
234SoundFilters_Array = {
235'habemus': 'https://dl.dropboxusercontent.com/s/3w1jahgffowjltz/hp.ogg',
236'lol': 'https://dl.dropboxusercontent.com/s/9aqa1buu3phvpss/laugh.ogg',
237}
238
239ModPanel_Array = [
240['', 'Welcome to the moderators panel. You can find here messages, news and help for all channel moderators, edited by script admin.'],
241['', 'To temporary insert to the playlist any website instead of media files, click "Embed a custom frame" button in the playlist controls section. Then paste example code to the textarea: <i><iframe src="URL_of_your_page"></iframe></i>, and add.'],
242];
243
244CustomCaptions_Array = {
245'add': 'Add here',
246'refresh': 'Refresh player',
247'voteskip': 'Voteskip',
248'welcome': 'Hi',
249}
250
251UnicodeChars_Array = [
252'★', '☆', '▲', '▼', '♥', '♪', '♿', '⚒', '♕', '✉', '☏', '♠', '→',
253'☑', '☒', '✡', '☪', '✝', '☭', '☯', 'Ⓐ', '☕', '♨', '¥', '©', '∞',
254];
255
256ChannelGalleries_Array = [
257['Anime pictures', 'http://imgur.com/a/SjwJb/embed'],
258['Historical photos', 'http://imgur.com/a/vnwC2/embed'],
259];
260
261 /* -- HTML/CSS -- */
262
263MOTDTabs_CSS = {
264'padding': '20px',
265'color': 'white',
266'background-color': 'black',
267}
268
269RulesBtn_HTML = '<ol><li>You want to write on the chat? Enter temporary nickname into <b>Guest Login</b> input and click enter.</li><li>You want to register a nick? Click <b>Account -> Profile</b> on the top of the channel, and fill the registration form. You don\'t need an email to register.</li><li>Do not spam.</li><li>You want to have this application on your own channel? Go to <a href="https://github.com/zimny-lech/CyTube-Plus">https://github.com/zimny-lech/CyTube-Plus</a>.</li></ol>';
270
271ChannelAnnouncement_HTML = 'This channel has been created with <a href="https://github.com/zimny-lech/CyTube-Plus" target="_blank">CyTube Plus 4.5</a>.';
272
273EmbeddingMedia_Images = 'a[href$=".jpg"], a[href$=".jpg:large"], a[href$=".jpeg"], a[href$=".JPG"], a[href$=".png"], a[href$=".tiff"], a[href$=".gif"]';
274
275EmbeddingMedia_Videos = 'a[href$=".webm"]';
276
277CustomFooter_HTML = 'This is custom footer.';
278
279CustomRightFooter_HTML = '';
280
281/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
282
283/* ----- THEMES CONFIGURATION ----- */
284
285// NOTES:
286// a) TopUserLogo item has 3 attributes: name, URL, maximum height.
287// Those images can be selected by user in Layout Configuration panel, and will be displayed on the channel top.
288
289ChannelThemeURL = 'https://dl.dropboxusercontent.com/s/l1qoep4su4xn5qz/chinska.css';
290
291ThemesCSS = [
292['Kobato', 'https://dl.dropboxusercontent.com/s/1r3twlb0loipybw/kobato.css'],
293['Celadon', 'https://dl.dropboxusercontent.com/s/iwarwhhph1ti2ek/celadon.css'],
294];
295
296TopUserLogo = [
297['cytube plus', 'https://dl.dropboxusercontent.com/s/7mrz85gl29eiiks/logo.png', 90],
298['anime girl', 'https://dl.dropboxusercontent.com/s/knxd7dpup1u8lm3/azuki.png', 200],
299['cosmos', 'https://dl.dropboxusercontent.com/s/v6dx49yqk5e3i2d/cosmos.jpg', 200],
300['disco ball', 'https://dl.dropboxusercontent.com/s/ahpfm25pglc8j01/disco.jpg', 162],
301['japanese landscape', 'https://dl.dropboxusercontent.com/s/llylt832evxrp6e/japan.jpg', 200],
302['korean collage', 'https://dl.dropboxusercontent.com/s/qud9adhs183dq30/korea.jpg', 160],
303['my little pony', 'https://dl.dropboxusercontent.com/s/r4ozo8oj8lmerec/mlp.jpg', 190],
304];
305
306EmptyCornerBackground = [
307'https://dl.dropboxusercontent.com/s/xa32t6jh68lor6p/kirino.png',
308'https://dl.dropboxusercontent.com/s/xbz6j5vjqs34ihm/kobato0.png',
309'https://dl.dropboxusercontent.com/s/sj67s6u5vwe6i7s/kuroneko.png',
310'https://dl.dropboxusercontent.com/s/y9nnwvbrcbjm0fm/tsukiko.png',
311'https://dl.dropboxusercontent.com/s/7bigihfi4ho0d29/moetan.png',
312'https://dl.dropboxusercontent.com/s/0cg72kjsnt4p63g/azuki.png',
313'https://dl.dropboxusercontent.com/s/va28xkaoz1co3ah/sakura.png',
314];
315
316/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
317
318/* ----- INDEPENDENT EMOTES AND FILTERS CONFIGURATION ----- */
319
320// NOTES ABOUT INDEPENDENT EMOTES:
321// Every item has 4 attributes, respectively: chat code, image URL, image width, image height.
322// Warning: due to conflict with RegExp, do not use square brackets or use proper "\\[" and "\\]" codes.
323
324IndependentEmotes = [
325[':awesome:', 'https://dl.dropboxusercontent.com/s/gz1k8oto90n16v6/awesome.png', 35, 35],
326[':love:', 'https://dl.dropboxusercontent.com/s/fr9131zgnai0kix/heart.png', 35, 35],
327];
328
329// NOTES AND DEFAULT FILTERS EXAMPLES:
330// If you use regular expression, you must put it between // signs with /g flag to change all occurences
331// a) [mq]text[/mq] - fast scrolling text
332// b) [mq0]text[/mq0] - slow scrolling text
333// c) [imgur]suok1xr.jpg[/imgur] - imgur picture
334// d) [drop]7mrz85gl29eiiks/logo.png[/drop] - dropbox picture
335// e) [minus/i5]ig0qs6fvWvgBu.jpg[/minus] - minus.com picture
336// f) [vi/b]1r8ih4t1.vichan.png[/vi] - vichan /b/ imageboard picture (you can use other boards too)
337
338IndependentFilters=[
339{
340before:/\[mq\](.*?)\[\/mq\]/g,
341after:'<marquee scrollamount="10" behavior="alternate">$1</marquee>'
342},
343{
344before:/\[mq0\](.*?)\[\/(mq0|mq)\]/g,
345after:'<marquee scrollamount="5" behavior="alternate">$1</marquee>'
346},
347{
348before:/\[imgur\](.*?)\[\/(i|imgur)\]/g,
349after:'<a href="http://i.imgur.com/$1" target="_blank"><img src="http://i.imgur.com/$1" style="max-width:160px"></a>'
350},
351{
352before:/\[drop\](.*?)\[\/(d|drop)\]/g,
353after:'<a href="https://dl.dropboxusercontent.com/s/$1" target="_blank">'
354 + '<img src="https://dl.dropboxusercontent.com/s/$1" style="max-width:160px"></a>'
355},
356{
357before:/\[minus\/(.*?)\](.*?)\[\/(m|minus)\]/g,
358after:'<a href="http://$1.minus.com/$2" target="_blank">'
359 + '<img src="http://$1.minus.com/$2" style="max-width:160px"></a>'
360},
361{
362before:/\[vi\/(.*?)\](.*?)\[\/(v|vi)\]/g,
363after:'<a href="https://pl.vichan.net/$1/src/$2" target="_blank">'
364 + '<img src="https://pl.vichan.net/$1/src/$2" style="max-width:160px"></a>'
365},
366];
367
368/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
369
370/* ----- MEDIA DATABASE CONFIGURATION ----- */
371
372// NOTES:
373// Leave empty URL field to create category button.
374// WARNING! Use ChannelDatabase_URL only if you want to use external database file, it overwrites database below.
375// If so, copy and configure 'https://raw.github.com/zimny-lech/CyTube-Plus/master/external-db.js' file.
376
377ChannelDatabase=[
378['', 'Japanese'],
379['http://www.youtube.com/watch?v=Q3y-80HBM6Q', 'An Cafe - Smile Ichiban Ii Onna'],
380['http://www.youtube.com/watch?v=nDqaTXqCN-Q', 'Babymetal - Ijime, Dame, Zettai'],
381['http://www.youtube.com/watch?v=aNlkiymcT1Q', 'Hinoi Team - Ike Ike'],
382['http://www.youtube.com/watch?v=JbQYK0mwLss', 'Morning Musume - Onna Ni Sachi Are'],
383['http://www.youtube.com/watch?v=VZKMaFR2mtU', 'Perfume - Fake It'],
384['http://www.youtube.com/watch?v=OYEHfWb1b4M', 'Shoko Nakagawa - Yuzurenai Negai'],
385['http://www.youtube.com/watch?v=jvhI576M6so', 'Yuu Kikkawa - Bokarisuto?'],
386['', 'Korean'],
387['http://www.youtube.com/watch?v=M-XXJJFZcXg', '4Minute - Heart To Heart (jap.)'],
388['http://www.youtube.com/watch?v=HchHZkkBOoE', 'As One - Catch Me Up'],
389['http://www.youtube.com/watch?v=UVeTI4v24M0', 'C-REAL - No No No No No'],
390['http://www.youtube.com/watch?v=04FdisNU3vw', 'Girl\'s Day - Oh! My God'],
391['http://www.youtube.com/watch?v=ysnUHXksic8', 'NS Yoon-G - Reason To Become A Witch'],
392['http://www.youtube.com/watch?v=mZUZdGCyLmw', 'Orange Caramel - Bangkok City'],
393['http://www.youtube.com/watch?v=r-MXLmNnarQ', 'TINY-G - Minimanimo'],
394['', 'Chinese'],
395['http://www.youtube.com/watch?v=Bi0tjyVfHOg', 'Amber Ann - Lian Ai Yingyuan Tuan'],
396['http://www.youtube.com/watch?v=dsU0Vu6aJhw', 'By2 - Love You Love You'],
397['http://www.youtube.com/watch?v=ovPhJim1EUc', 'Momo - One Hundred Girlfriends '],
398['http://www.youtube.com/watch?v=lxa2KqERBZE', 'S.H.E. - Shero'],
399['http://www.youtube.com/watch?v=jd2-Nf86jvA', 'Weather Girls - Koi No Tenki Yohou'],
400['', 'Anime songs'],
401['http://www.youtube.com/watch?v=fJ_DH7jzoxQ', 'Ai No Tenshi (Perfect Blue)'],
402['http://www.youtube.com/watch?v=ZNu_NV5PEM8', 'Chu☆ - Miko Miko Naasu - Ai No Teema'],
403['http://www.youtube.com/watch?v=l65_QPNZ_k8', 'Hommarju feat. MAKI - Yamato Nadeshiko Education (Seitokai Yakuindomo OP)'],
404['http://www.youtube.com/watch?v=dQbaJquz_jo', 'Kayo Sakata - Shoujo Q (Pani Poni Dash! OP)'],
405['http://www.youtube.com/watch?v=nLSFxQijz6U', 'Kotoko - Princess Bride'],
406['http://www.youtube.com/watch?v=QK-37CnYTpg', 'Princess Party Camelia OP'],
407['http://www.youtube.com/watch?v=jehMXrY1q5I', 'Yui Makino - Yume No Tsubasa'],
408];
409
410ChannelDatabase_URL='';
411
412/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
413
414/* ----- END OF CONFIGURATION, DO NOT CHANGE ANYTHING BELOW ----- */
415
416/* ----- Initial channel options ----- */
417
418/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
419
420// reload script after unexpected re-connection or script URL change
421
422var LOADED = (typeof LOADED==="undefined") ? false : true;
423LOADED ? location.reload() : '';
424
425/* ----- getting and setting channel options ----- */
426
427if (UI_DefaultSynchtube=="1") {
428 defplayer="left"; defuserlist="right"; defqueue="left";
429} else {
430 defplayer="right"; defuserlist="left"; defqueue="right";
431}
432
433var DEFTHEME = (UI_ChannelTheme=="1" && ChannelThemeURL!="") ? ChannelThemeURL : "/css/themes/slate.css";
434
435var USERCONFIG = {
436 "player":getOrDefault(CHANNEL.name+"_player", defplayer),
437 "userlist":getOrDefault(CHANNEL.name+"_userlist", defuserlist),
438 "queue":getOrDefault(CHANNEL.name+"_queue", defqueue),
439 "qsize":getOrDefault(CHANNEL.name+"_qsize", "wide"),
440 "main":getOrDefault(CHANNEL.name+"_main", "top"),
441 "motd":getOrDefault(CHANNEL.name+"_motd", "top"),
442 "logo":getOrDefault(CHANNEL.name+"_logo", "no"),
443 "logourl":getOrDefault(CHANNEL.name+"_logourl", ""),
444 "logoht":getOrDefault(CHANNEL.name+"_logoht", 250),
445 "header":getOrDefault(CHANNEL.name+"_header", "detached"),
446 "css":getOrDefault(CHANNEL.name+"_css", "no"),
447 "csscode":getOrDefault(CHANNEL.name+"_csscode", ""),
448 "modhash":getOrDefault(CHANNEL.name+"_modhash", ""),
449}
450var USERTHEME = getOrDefault(CHANNEL.name+"_theme", DEFTHEME);
451var FLUID = getOrDefault(CHANNEL.name+"_fluid", false);
452var LAYOUTBOX = getOrDefault(CHANNEL.name+"_layoutbox", true);
453var SOUNDSLVL = getOrDefault(CHANNEL.name+"_soundslvl", 3);
454var EMBEDIMG = getOrDefault(CHANNEL.name+"_embedimg", true);
455var EMBEDVID = getOrDefault(CHANNEL.name+"_embedvid", true);
456var AUTOVID = getOrDefault(CHANNEL.name+"_autovid", true);
457var USERVISITS = getOrDefault(CHANNEL.name+"_visits", 0);
458
459var DEFDESCR = true; // standard item description in the player header
460var NOPLAYER = false; // removed player in silent mode
461var CHATFUNC = false; // admin chat functions panel visibility
462var COMMAND = false; // aditional command occuring in the chat message
463var VOICES = false; // chat sounds not disabled by user
464var EMOTES = false; // emotes have been loaded into emotes panel
465var CLEARING = false; // auto clearing messages window
466var ANTIAFK = false; // enabled anti-AFK function
467var SOUNDSPANEL = false; // chat sounds panel visibility
468var PINNED = false; // playlist pinned to player
469var FULLPL = false; // expanded playlist view
470var MINIMIZED = false; // minimized layout
471var CHANDB = false; // channel database has been loaded
472var GALLERY = false; // channel galleries have been loaded
473var GALLVIS = false; // channel galleries have been viewed by user
474var ALTERCHATFORMAT = false; // using altered 'formatChatMessage' built-in function
475
476var PREVTIME = 0; // previous read of a current item time for the progress bar
477var LASTADD = 0; // timestamp of the last adding random item from the channel database
478var USERONLINE = 0; // user minutes online
479var BGCHANGE = 0; // number of background changes for the easter egg function
480
481var MUTEDVOICES = new Array(); // list of users with muted chat sounds by user
482var CHATSTAT = {"n":0, "l":0, "m":[]}; // array with user messages statistics
483var ADDEDLINKS = new Array(); // array of links added from channel database by user
484
485var HASH = ''; // simple hash for comparing if the new messages have appeared in the mod panel
486
487WEBKIT="webkitRequestAnimationFrame" in window;
488SOUNDSVALUES=new Array(0, 0.1, 0.2, 0.4, 0.7, 1);
489SPEAKLINK='http://webanywhere.cs.washington.edu/cgi-bin/espeak/getsound.pl';
490IMBA=new Audio("https://dl.dropboxusercontent.com/s/xdnpynq643ziq9o/inba.ogg");
491CHATSOUND.volume=0.6;
492
493/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
494
495/* ----- Global functions ----- */
496
497/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
498
499// toggle elements visibility
500
501function toggleDiv(div) {
502 $(div).css('display')=="none" ? $(div).show() : $(div).hide();
503}
504
505// refresh player
506
507function refreshPlayer() {
508 PLAYER.type="";
509 PLAYER.id="";
510 socket.emit("playerReady");
511}
512
513// add link to playlist
514
515function addToPlaylist(link, stand) {
516 parsed=parseMediaLink(link);
517 parsed["id"]!=null ? socket.emit("queue", {id:parsed["id"], pos:stand, type:parsed["type"]}) : '';
518}
519
520// get text content from inner HTML
521
522function getText(html) {
523 div=document.createElement("div");
524 div.innerHTML=html;
525 return div.textContent||div.innerText;
526}
527
528// create modal window
529
530function createModal(title) {
531 hidePlayer();
532 outer = $('<div class="modal fade" />').appendTo($("body"));
533 modal = $('<div class="modal-dialog" />').appendTo(outer);
534 modal = $('<div class="modal-content" />').appendTo(modal);
535 head = $('<div class="modal-header" />').appendTo(modal);
536 $('<button class="close" data-dismiss="modal" aria-hidden="true" />').html('×').appendTo(head);
537 $('<h3 />').text(title).appendTo(head);
538 body = $('<div class="modal-body" />').appendTo(modal);
539 footer = $('<div class="modal-footer" />').appendTo(modal);
540 outer.on("hidden", function() {
541 outer.remove();
542 unhidePlayer();
543 });
544 outer.modal();
545}
546
547// layout elements settings
548
549function playerLocation(a) {
550 $("#pinup-btn").show();
551 if (a=="left") {
552 $("#videowrap").after($("#chatwrap").detach());
553 normalPlayer();
554 normalChat();
555 } else if (a=="right") {
556 $("#videowrap").before($("#chatwrap").detach());
557 normalPlayer();
558 normalChat();
559 } else if (a=="center") {
560 $("#videowrap").after($("#chatwrap").detach());
561 $("#videowrap, #chatwrap").removeClass().addClass("col-lg-8 col-lg-offset-2 col-md-12");
562 fitPlayer();
563 fitChat(200);
564 $("#pinup-btn").hide();
565 }
566}
567
568function userlistLocation(a) {
569 a=="left" ? $("#userlist").css('float', 'left') : $("#userlist").css('float', 'right');
570}
571
572function queueLocation(a) {
573 $("#pinup-btn").show();
574 if (a=="right") {
575 $("#rightpane").before($("#leftpane").detach());
576 } else if (a=="left") {
577 $("#rightpane").after($("#leftpane").detach());
578 } else if (a=="center") {
579 $("#rightpane").after($("#leftpane").detach())
580 .removeClass().addClass('col-md-8 col-md-offset-2 col-md-12');
581 $("#leftpane").removeClass().addClass('col-md-8 col-md-offset-2 col-md-12');
582 $("#pinup-btn").hide();
583 }
584 b = (a=="right") ? "left" : "right";
585 $("#playlistrow").css('background-position', b+' bottom');
586}
587
588function queueSize(a) {
589 if (USERCONFIG.queue!="center") {
590 if (a=="wide") {
591 $("#leftpane").removeClass().addClass('col-lg-5 col-md-5');
592 $("#rightpane").removeClass().addClass('col-lg-7 col-md-7');
593 } else if (a=="narrow") {
594 $("#leftpane").removeClass().addClass('col-lg-7 col-md-7');
595 $("#rightpane").removeClass().addClass('col-lg-5 col-md-5');
596 }
597 }
598}
599
600function mainLocation(a) {
601 if (a=="top") {
602 $("#main").before($("#titlerow").detach()).after($("#playlistrow").detach());
603 } else if (a=="bottom") {
604 $("#main").before($("#playlistrow").detach()).before($("#titlerow").detach());
605 }
606 $("#main").after($("#chatpanel").detach());
607}
608
609function motdLocation(a) {
610 if (a=="top") {
611 $("#zerorow").after($("#announcements").detach()).after($("#motdrow").detach());
612 } else if (a=="bottom") {
613 $("#resizewrap").before($("#motdrow").detach()).before($("#announcements").detach());
614 }
615}
616
617function logoInsert(a) {
618 if (a!="no") {
619 link = (a!="user") ? TopUserLogo[a][1] : USERCONFIG.logourl;
620 ht = (a!="user") ? TopUserLogo[a][2] : USERCONFIG.logoht;
621 azukirow.css({'min-height':ht+'px', 'background-image':'url("'+link+'")'});
622 } else if (a=="no") {
623 azukirow.css({'min-height':'5px', 'background-image':''});
624 }
625}
626
627function headerMode(a) {
628 $(".navbar-fixed-top").unbind();
629 if (a=="fixed") {
630 $(".navbar-fixed-top").css({'position': 'fixed', 'top':'0px'});
631 $("#mainpage").css('margin-top', '0px');
632 } else if (a=="detached") {
633 $(".navbar-fixed-top").css('position', 'inherit');
634 $("#mainpage").css('margin-top', '-72px');
635 } else if (a=="mouseover") {
636 $(".navbar-fixed-top").css({'position':'fixed', 'top':'-40px'})
637 .on("mouseover", function() {
638 $(".navbar-fixed-top").css('top', '0px');
639 })
640 .on("mouseout", function() {
641 $(".navbar-fixed-top").css('top', '-40px');
642 });
643 $("#mainpage").css('margin-top', '-40px');
644 }
645}
646
647function customCSS(a) {
648 $("#usercss").remove();
649 a=="yes" ? $("head").append('<style id="usercss" type="text/css">'+USERCONFIG.csscode+'</style>') : '';
650}
651
652// set global layout according to user preferences
653
654function setLayout() {
655 playerLocation(USERCONFIG.player);
656 userlistLocation(USERCONFIG.userlist);
657 queueLocation(USERCONFIG.queue);
658 queueSize(USERCONFIG.qsize);
659 mainLocation(USERCONFIG.main);
660 motdLocation(USERCONFIG.motd);
661 logoInsert(USERCONFIG.logo);
662 headerMode(USERCONFIG.header);
663 customCSS(USERCONFIG.css);
664}
665
666// fit player height
667
668function fitPlayer() {
669 VW=$("#videowrap").width()+'';
670 VH=Math.floor(parseInt(VW)*9/16+1)+'';
671 $("#ytapiplayer").width(VW).height(VH);
672}
673
674// fit chat height
675
676function fitChat(a) {
677 if (a=="auto") {
678 VW=$("#messagebuffer").width();
679 VH=Math.floor(parseInt(VW)*9/16+1);
680 } else {
681 VH=a;
682 }
683 $("#messagebuffer").height(VH);
684 $("#userlist").height(VH);
685}
686
687// display mode helper functions
688
689function bigPlayer() {
690 $("#videowrap").removeClass().addClass("col-lg-12 col-md-12");
691 fitPlayer();
692}
693
694function bigChat() {
695 $("#chatwrap").removeClass().addClass('col-lg-12 col-md-12');
696 fitChat("auto");
697}
698
699function normalPlayer() {
700 $("#videowrap").removeClass().addClass("col-lg-7 col-md-7");
701 fitPlayer();
702}
703
704function normalChat() {
705 c = (PINNED && USERCONFIG.qsize=="wide") ? 'col-lg-7 col-md-7' : 'col-lg-5 col-md-5';
706 $("#chatwrap").removeClass().addClass(c);
707 fitChat(338);
708}
709
710// set display mode
711
712function setMode(a) {
713 if (NOPLAYER) {
714 $("#videowrap").show();
715 ytapiplayer = $('<div id="ytapiplayer" />')
716 .insertBefore("#playercontrols");
717 refreshPlayer();
718 NOPLAYER = false;
719 }
720
721 $("#main").show();
722 pinupbtn.hide();
723 modesel.find("option[value='chMode'], option[value='rMode']").show();
724 PINNED ? modesel.find("option[value='chMode']").hide() : '';
725
726 if (a=="syMode") {
727
728 $("#videowrap, #videowrap p, #videowrap div, #chatwrap, #rightpane, #pinup-btn").show();
729 $("#config-btn, #configbtnwrap br, #pinup-btn").show();
730 $("#min-layout").parent().show();
731
732 normalPlayer();
733
734 c = (PINNED && USERCONFIG.qsize=="wide") ? 'col-lg-7 col-md-7' : 'col-lg-5 col-md-5';
735 $("#chatwrap").removeClass().addClass(c);
736 H=parseInt(VH)-$("#chatline").outerHeight()-1;
737 $("#messagebuffer").height(H);
738 $("#userlist").height(H);
739
740 USERCONFIG.player=="center" ? playerLocation("center") : '';
741 PINNED ? pinUp() : '';
742
743 } else if (a=="kMode") {
744
745 $("#videowrap").show();
746 PINNED ? $("#rightpane").hide() : $("#chatwrap").hide();
747 $("#fontspanel, #emotespanel").hide();
748
749 bigPlayer();
750
751 } else if (a=="chMode") {
752
753 $("#chatwrap").show();
754
755 if (WEBKIT) {
756 $("#videowrap").hide();
757 } else {
758 $("#videowrap div, #videowrap p").hide();
759 $("#ytapiplayer").width(1).height(1);
760 }
761
762 bigChat();
763
764 } else if (a=="sMode") {
765
766 $("#chatwrap").show();
767 $("#videowrap").hide();
768 $("#ytapiplayer").remove();
769 $("#fontspanel, #emotespanel").hide();
770 modesel.find("option[value='chMode'], option[value='rMode']").hide();
771 NOPLAYER = true;
772
773 bigChat();
774
775 } else if (a=="rMode") {
776
777 if (WEBKIT) {
778 $("#main").hide();
779 } else {
780 PINNED ? $("#rightpane").hide() : $("#chatwrap").hide();
781 $("#videowrap div, #videowrap p").hide();
782 $("#ytapiplayer").width(1).height(1);
783 }
784
785 !PINNED ? $("#min-layout").parent().show() : '';
786
787 }
788}
789
790// fix layout after changing media
791
792function setModeAfterVideoChange() {
793 m=modesel.val();
794 (m=="syMode" || m=="chMode" || m=="rMode") ? setMode(m) : '';
795}
796
797// change welcome text
798
799function changeWelcomeText() {
800 if (CLIENT.rank>0) {
801 $("#welcome").html($("#welcome").html().replace(/Welcome/, CustomCaptions_Array['welcome']));
802 }
803}
804
805// set MOTD
806
807function changeMOTD() {
808 if (UI_MOTDTabs=="1" && MOTDTabs_Array.length>0) {
809 // adding tabs application
810
811 motdtabswrap = $('<div id="motdtabswrap" />')
812 .appendTo("#motd");
813 for (i in MOTDTabs_Array) {
814 btn = $('<button class="btn btn-default motdtabs-btn" tab="'+i+'">')
815 .text(MOTDTabs_Array[i][0])
816 .appendTo(motdtabswrap)
817 .on("click", function() {
818 $(".motdtabs-btn").removeClass('btn-success');
819 $(this).addClass('btn-success');
820 nr=$(this).attr('tab');
821 motdtabscontent.html(MOTDTabs_Array[nr][1]);
822 });
823 }
824 motdtabscontent = $('<div id="motdtabscontent">'+MOTDTabs_Array[0][1]+'</div>')
825 .css(MOTDTabs_CSS)
826 .appendTo("#motd");
827 $(".motdtabs-btn:nth-child(1)").addClass('btn-success');
828 }
829 if (UI_MOTDAutoLogo=="1") {
830 // adding logo
831
832 var logo = 0;
833 var len = MOTDAutoLogo_Array.length;
834 if (len<1) {
835 MOTDAutoLogo_Array=['https://dl.dropboxusercontent.com/s/7mrz85gl29eiiks/logo.png'];
836 len=1;
837 }
838 if (MOTDAutoLogo_Mode=="2" || MOTDAutoLogo_Mode=="3") {
839 logo=Math.floor(Math.random()*len);
840 } else if (MOTDAutoLogo_Mode=="7") {
841 logo=new Date().getDay();
842 typeof MOTDAutoLogo_Array[logo]==="undefined" ? logo=0 : '';
843 }
844 $('<center><img id="motdlogo" src="'+MOTDAutoLogo_Array[logo]+'" /></center>').prependTo("#motd");
845 }
846 if (UI_RulesBtn=="1") {
847 // adding rules button
848
849 RulesBtn_Caption=="" ? RulesBtn_Caption='Read Channel Rules' : '';
850 RulesBtn_HTML=="" ? RulesBtn_HTML='No rules.' : '';
851 rulesbtnwrap = $('<div id="rulesbtnwrap" />').appendTo("#motd");
852 rulesbtn = $('<button id="rules-btn" class="btn btn-default btn-sm" />')
853 .text(RulesBtn_Caption+' ▸')
854 .appendTo(rulesbtnwrap)
855 .on("click", function() {
856 toggleDiv(rulespanel);
857 });
858 rulespanelouter = $('<div id="rulespanel-outer" />').appendTo("#motd");
859 rulespanel = $('<div id="rulespanel" style="display:none" />')
860 .html(RulesBtn_HTML)
861 .appendTo(rulespanelouter);
862 }
863}
864
865// change title bar description
866
867function changeTitle() {
868 title=$("#currenttitle").text();
869 $("#currenttitle").text(title.replace(/Currently Playing:/, TitleBarDescription_Caption));
870}
871
872// YouTube/Dailymotion progress bar
873
874function progressBar() {
875 var a = 0;
876 if (PLAYER.type=="yt") {
877 b=PLAYER.player.getCurrentTime();
878 b!=PREVTIME ? a=b/PLAYER.player.getDuration()*100 : '';
879 PREVTIME=b;
880 } else if (PLAYER.type=="dm") {
881 b=PLAYER.player.currentTime;
882 b!=PREVTIME ? a=b/PLAYER.player.duration*100 : '';
883 PREVTIME=b;
884 }
885 titlerow.css('background-size', a+'% 100%');
886}
887
888// toggle additional chat functions
889
890function toggleChatFunctions() {
891 CLIENT.rank>2 ? chatflair.show() : chatflair.hide();
892}
893
894// set chat side panel properties
895
896function setPanelProperties(div) {
897 bgcolor=$("body").css('background-color');
898 color=$("body").css('color');
899 height=$("#userlist").height();
900 width=$("#userlist").width();
901 $(div).css({'background-color':bgcolor, 'color':color, 'height':height+'px', 'width':width+'px'});
902}
903
904// refresh user chat statistics
905
906function userChatStats(str) {
907 CHATSTAT['n']++;
908 CHATSTAT['l']=CHATSTAT['l']+str.length;
909 CHATSTAT['m'].push(str);
910}
911
912// create squavatar
913
914function createSquavatar(str) {
915 for (i=0, hash=0; i<str.length; hash=str.charCodeAt(i++)+((hash<<5)-hash));
916 for (i=0, col=''; i<3; col+=('00'+((hash>>i++*8)&0xFF).toString(16)).slice(-2));
917 r=parseInt(col.substring(0,2), 16);
918 g=parseInt(col.substring(2,4), 16);
919 b=parseInt(col.substring(4,6), 16);
920 r2=255-r;
921 g2=255-g;
922 b2=255-b;
923 html='<span class="squavatar" style="background-color:rgb('+r+','+g+','+b+'); '
924 + 'border-color:rgb('+r2+','+g2+','+b2+')"></span>';
925 return html;
926}
927
928// format chat messages before sending and execute commands
929
930function prepareMessage(msg) {
931 if (UI_MessagesSuffix=="1") {
932 if ((typeof MessagesSuffix_Percentage!=="number") || MessagesSuffix_Percentage<0) {
933 MessagesSuffix_Percentage='10';
934 }
935 Math.random()<(MessagesSuffix_Percentage/100) ? msg+=' '+MessagesSuffix_Text : '';
936 }
937
938 if (UI_UserCommands=="1" && msg.indexOf("!")==0) {
939 COMMAND=true;
940 if (msg.indexOf("!stat")==0) {
941
942 num=CHATSTAT['n'];
943 len=CHATSTAT['l'];
944 if (num>0) {
945 rnd=Math.round(Math.random()*(CHATSTAT['m'].length-1));
946 avg=Math.round(len/num*10)/10;
947 } else {
948 rnd=0;
949 avg=0;
950 }
951 a = (num!=1) ? 's' : '';
952 b = (avg!=1) ? 's' : '';
953 msg='you have sent '+num+' message'+a+', '
954 +'total length is '+len+' character'+b+' ('+avg+' per message), '
955 +'random message: '+CHATSTAT['m'][rnd];
956
957 } else if (msg.indexOf("!memestats")==0) {
958
959 num=CHATSTAT['n'];
960 len=Memes_Array.length;
961 mem=0;
962 for (i=0; i<num; i++) {
963 for (j=0; j<len; j++) {
964 CHATSTAT['m'][i].indexOf(Memes_Array[j])>-1 ? mem++ : '';
965 }
966 }
967 a = (num!=1) ? 's' : '';
968 b = (mem!=1) ? 's' : '';
969 if (len>0) {
970 msg='in '+num+' message'+a+' you have used '+mem+' meme'+b;
971 } else {
972 msg='error: no defined memes';
973 }
974
975 } else if (msg.indexOf("!pick ")==0) {
976
977 arr=msg.split("!pick ")[1].split(",");
978 rnd=Math.round(Math.random()*(arr.length-1));
979 msg=arr[rnd];
980
981 } else if (msg.indexOf("!ask ")==0) {
982
983 AskAnswers_Array.length<1 ? AskAnswers_Array=['yes', 'no'] : '';
984 rnd=a=Math.round(Math.random()*(AskAnswers_Array.length-1));
985 msg=AskAnswers_Array[rnd];
986
987 } else if (msg.indexOf("!time")==0) {
988
989 var h = new Date().getHours();
990 h<10 ? h='0'+h : '';
991 var m = new Date().getMinutes();
992 m<10 ? m='0'+m : '';
993 msg='current time: '+h+':'+m;
994
995 } else if (msg.indexOf("!dice")==0) {
996
997 rnd=Math.round(Math.random()*5)+1;
998 msg=''+rnd;
999
1000 } else if (msg.indexOf("!roll")==0) {
1001
1002 var rnd = Math.round(Math.random()*999);
1003 rnd<100 ? rnd="0"+rnd : '';
1004 rnd<10 ? rnd="0"+rnd : '';
1005 msg=''+rnd;
1006
1007 } else if (msg.indexOf("!q")==0) {
1008
1009 RandomQuotes_Array.length<1 ? RandomQuotes_Array=['error: no quotes available'] : '';
1010 rnd=Math.round(Math.random()*(RandomQuotes_Array.length-1));
1011 msg=RandomQuotes_Array[rnd];
1012
1013 } else if (msg.indexOf("!random")==0 && hasPermission("playlistadd")) {
1014
1015 if (UI_ChannelDatabase=="1") {
1016 var link="";
1017 while (link=="") {
1018 rnd=Math.round(Math.random()*(ChannelDatabase.length-1));
1019 link=ChannelDatabase[rnd][0];
1020 title=ChannelDatabase[rnd][1];
1021 }
1022 addToPlaylist(link, "end");
1023 msg='random media adding attempt: '+title;
1024 }
1025
1026 } else if (msg.indexOf("!calc ")==0) {
1027
1028 func=msg.split("!calc ");
1029 msg=''+eval(func[1]);
1030
1031 } else if (msg.indexOf("!skip")==0 && hasPermission("voteskip")) {
1032
1033 socket.emit("voteskip");
1034 msg='current item has been voteskipped';
1035
1036 } else if (msg.indexOf("!next")==0 && hasPermission("playlistjump")) {
1037
1038 socket.emit("playNext");
1039 msg='start playing next item';
1040
1041 } else if (msg.indexOf("!bump")==0 && hasPermission("playlistmove")) {
1042
1043 last=$("#queue").children().length;
1044 uid=$("#queue .queue_entry:nth-child("+last+")").data("uid");
1045 title=$("#queue .queue_entry:nth-child("+last+") .qe_title").html();
1046 socket.emit("moveMedia", {from:uid, after:PL_CURRENT});
1047 msg='last item bumped as next: '+title;
1048
1049 } else if (msg.indexOf("!add ")==0 && hasPermission("playlistadd")) {
1050
1051 parsed=parseMediaLink(msg.split("!add ")[1]);
1052 if (parsed["id"]===null) {
1053 msg='error: wrong link, item has not been added';
1054 } else {
1055 socket.emit("queue", {id:parsed["id"], pos:"end", type:parsed["type"]});
1056 msg='video has been added';
1057 }
1058
1059 } else if (msg.indexOf("!now")==0) {
1060
1061 msg='Now playing: '+$(".queue_active a").html();
1062
1063 } else if (msg.indexOf("!CO ZJE TEH?")==0) {
1064
1065 msg='TEH ZJE HUJ';
1066
1067 } else if (msg.indexOf("!inba")==0) {
1068
1069 IMBA.volume=0.6;
1070 IMBA.play();
1071 mutePlayer();
1072 inbix=setInterval(function() {
1073 inba();
1074 }, 200);
1075 setTimeout(function() {
1076 unmutePlayer();
1077 BGCHANGE=0;
1078 clearInterval(inbix);
1079 $("body").css({'background-image':'', 'background-color':''});
1080 setUserCSS();
1081 }, 12000);
1082 msg='JP2GMD';
1083
1084 } else {
1085 COMMAND=false;
1086 }
1087 }
1088 return msg;
1089}
1090
1091// insert code into chatline
1092
1093function insertText(str) {
1094 $("#chatline").val($("#chatline").val()+str).focus();
1095}
1096
1097// toggle YT mute button
1098
1099function toggleMuteBtn() {
1100 (PLAYER && PLAYER.type=="yt") ? muteplayerbtn.show() : muteplayerbtn.hide();
1101}
1102
1103// toggle mod panel button
1104function toggleModPanel() {
1105 if (CLIENT.rank<2) {
1106 modbtn.hide();
1107 } else {
1108 modbtn.show();
1109 HASH='';
1110 for (i in ModPanel_Array) {
1111 name=ModPanel_Array[i][0];
1112 (name=="" || name==CLIENT.name) ? HASH+=''+ModPanel_Array[i][1].length : '';
1113 }
1114 if (HASH!=USERCONFIG.modhash) {
1115 modbtn.addClass('btn-danger').html(modbtn.html()+' (New Mess.)');
1116 }
1117 }
1118}
1119
1120// create media database
1121
1122function createDatabase() {
1123 html='<button id="la1" class="btn btn-default btn-sm db-break" onclick="toggleCat(1)">'
1124 + ChannelDatabase[0][1]
1125 + '</button>'
1126 + '<ul id="l1" class="videolist db-cat">';
1127
1128 len=ChannelDatabase.length;
1129 for (i=1; i<ChannelDatabase.length; i++) {
1130 if (ChannelDatabase[i][0]=="") {
1131 item_count[layer_nr-1]=count_nr;
1132 opening[layer_nr-1]=0;
1133 layer_nr++;
1134 count_nr=0;
1135
1136 html+='</ul>'
1137 + '<button id="la'+layer_nr+'" class="btn btn-default btn-sm db-break" '
1138 + 'onclick="toggleCat('+layer_nr+')">'
1139 + ChannelDatabase[i][1]
1140 + '</button>'
1141 + '<ul id="l'+layer_nr+'" class="videolist db-cat">';
1142 } else {
1143 item_nr++;
1144 count_nr++;
1145 link=ChannelDatabase[i][0];
1146
1147 html+='<li class="queue_entry">'
1148 + '<button class="btn btn-default btn-xs pull-right" onclick="addVideo(\''+link+'\')">'
1149 + 'End'
1150 + '</button>';
1151
1152 parsed=parseMediaLink(link);
1153 if (parsed["type"]=="yt") {
1154 a=parsed["id"];
1155 html+='<button class="btn btn-default btn-xs pull-right" '
1156 + 'onclick="prevVideo(\''+a+'\')">'
1157 + '<i class="glyphicon glyphicon-film"></i>'
1158 + '</button>';
1159 }
1160 html+='<span class="badge db-badge">'
1161 + item_nr
1162 + '</span><span class="db-title">'
1163 + ChannelDatabase[i][1]
1164 + '</span><br /><span class="db-link">'
1165 + link
1166 + '</span>';
1167 }
1168 }
1169
1170 html+='</ul><br /><br /><div id="cleardbwrap">'
1171 + '<button id="cleardb-btn" class="btn btn-default btn-sm">'
1172 + 'Reload Data (if channel seems working slow)</button>'
1173 + '</div>';
1174
1175 item_count[layer_nr-1]=count_nr;
1176 opening[layer_nr-1]=0;
1177 dbwell.html(html);
1178 html='';
1179
1180 len=item_count.length;
1181 for(i=1; i<=len; i++) {
1182 $("#la"+i).append(' ['+item_count[i-1]+']');
1183 }
1184
1185 cleardbbtn = $("#cleardb-btn")
1186 .on("click", function() {
1187 toggleDiv(dbwrap);
1188 dbwell.html('');
1189 CHANDB=false;
1190 });
1191
1192 for (i in opening) {
1193 opening[i]=0;
1194 }
1195 dbcat = $(".db-cat").hide();
1196 CHANDB=true;
1197}
1198
1199// toggle database sections
1200
1201function toggleCat(a) {
1202 b=a-1;
1203 if (opening[b]==0) {
1204 dbcat.hide();
1205 for (i in opening) {
1206 opening[i]=0;
1207 }
1208 $("#l"+a).show();
1209 opening[b]=1;
1210 } else {
1211 $("#l"+a).hide();
1212 opening[b]=0;
1213 }
1214}
1215
1216// patch layout for guest logins
1217
1218function patchWrap() {
1219 setTimeout(function() {
1220 $("#playlistmanagerwrap").show();
1221 }, 1500);
1222}
1223
1224// create channel gallery
1225
1226function createGallery() {
1227 galleryframe = $('<iframe id="galleryFrame" src='+ChannelGalleries_Array[0][1]+' width="100%" />')
1228 .attr({'frameborder':'0', 'scrolling':'no', 'height':'415px'})
1229 .appendTo(gallerywell);
1230 galtitle = $('<span id="galtitle" class="conf-opt">Select Gallery</span>').appendTo(gallerywell);
1231 galsel = $('<select id="gal-sel" class="form-control" />')
1232 .appendTo(gallerywell)
1233 .on("change", function() {
1234 galleryframe.attr('src', galsel.val());
1235 });
1236
1237 for (i in ChannelGalleries_Array) {
1238 $('<option value="'+ChannelGalleries_Array[i][1]+'" />')
1239 .html(ChannelGalleries_Array[i][0])
1240 .appendTo(galsel);
1241 }
1242
1243 text='Reload Galleries (if problems or slow channel)';
1244 cleargallerybtn = $('<button id="cleargallery-btn" class="btn btn-default btn-sm">'+text+'</button>')
1245 .appendTo(gallerywell)
1246 .on("click", function() {
1247 toggleDiv(gallerywrap);
1248 gallerywell.html('');
1249 GALLERY=false;
1250 });
1251 GALLERY=true;
1252}
1253
1254// toggle "/clear" button depends on rank
1255
1256function toggleClearBtn() {
1257 hasPermission("chatclear") ? clearbtn.show() : 'clearbtn.hide()';
1258}
1259
1260// toggle YT volume buttons depends on player type
1261
1262function toggleVolBtn() {
1263 if (PLAYER && PLAYER.type=="yt") {
1264 voldownbtn.show();
1265 volupbtn.show();
1266 } else {
1267 voldownbtn.hide();
1268 volupbtn.hide();
1269 }
1270}
1271
1272// toggle advanced playlist options buttons
1273
1274function toggleAdvancedPl() {
1275 CLIENT.rank<2 ? advplaylist.hide() : advplaylist.show();
1276 hasPermission("playlistjump") ? playnextbtn.show() : playnextbtn.hide();
1277 (hasPermission("playlistadd") && UI_ChannelDatabase=="1") ? addrandombtn.show() : addrandombtn.hide();
1278 hasPermission("playlistmove") ? bumplastbtn.show() : bumplastbtn.hide();
1279 hasPermission("playlistdelete") ? deletelastbtn.show() : deletelastbtn.hide();
1280}
1281
1282// get playlist helper functions
1283
1284function formatRawList() {
1285 len=$("#queue .queue_entry").length+1;
1286 list = new Array();
1287 for (i=1; i<len; i++) {
1288 item=$("#queue .queue_entry:nth-child("+i+")").data("media");
1289 list.push(formatURL(item));
1290 }
1291 return list.join(",");
1292}
1293
1294function formatPlainTextList() {
1295 len=$("#queue .queue_entry").length+1;
1296 var list = new Array();
1297 for (i=1; i<len; i++) {
1298 item=$("#queue .queue_entry:nth-child("+i+")").data("media");
1299 list.push(i+'. '+formatURL(item)+' \/\/ '+item.title+' ['+item.duration+']');
1300 }
1301 return list.join('\n');
1302}
1303
1304function formatHTMLList() {
1305 len=$("#queue .queue_entry").length+1;
1306 var list = new Array();
1307 for (i=1; i<len; i++) {
1308 item=$("#queue .queue_entry:nth-child("+i+")").data("media");
1309 title=item.title;
1310 duration=item.duration;
1311 link=formatURL(item);
1312 list.push('<li>'+title+' ['+duration+'] - <a href="'+link+'" target="_blank">'+link+'</a></li>');
1313 }
1314 return list.join('\n');
1315}
1316
1317function formatOrderedList() {
1318 len=$("#queue .queue_entry").length+1;
1319 var list = new Array();
1320 for (i=1; i<len; i++) {
1321 item=$("#queue .queue_entry:nth-child("+i+")").data("media");
1322 link=formatURL(item);
1323 list.push(item.title+' ■■ '+link+' ■■ ['+item.duration+']');
1324 list.sort();
1325 }
1326 return list.join('\n');
1327}
1328
1329function formatDBList() {
1330 len=$("#queue .queue_entry").length+1;
1331 var list = new Array();
1332 for (i=1; i<len; i++) {
1333 item=$("#queue .queue_entry:nth-child("+i+")").data("media");
1334 re1=new RegExp('\\\\', 'g');
1335 re2=new RegExp('\'', 'g');
1336 title=item.title.replace(re1, '\\\\').replace(re2, '\\\'');
1337 list.push('[\''+formatURL(item)+'\', \''+title+'\'],');
1338 }
1339 return list.join('\n');
1340}
1341
1342// change voteskip caption
1343
1344function changeSkipText() {
1345 $("#voteskip").text(CustomCaptions_Array['voteskip']+' '+$("#voteskip").text());
1346}
1347
1348// add database link to playlist
1349
1350function addVideo(link) {
1351 parsed=parseMediaLink(link);
1352 idp=parsed["id"];
1353 if (idp!=null) {
1354 time=(new Date()).getTime();
1355 if (!hasPermission("playlistadd")) {
1356 alert('You have no permission to add a link.');
1357 } else if (ADDEDLINKS[idp]!=undefined && time-ADDEDLINKS[idp]<120000) {
1358 alert('You have just added this link.');
1359 } else {
1360 socket.emit("queue", {id:idp, pos:"end", type:parsed["type"]});
1361 ADDEDLINKS[idp]=time;
1362 }
1363 }
1364}
1365
1366// set user online time
1367
1368function onlineTime() {
1369 USERONLINE++;
1370 var h = Math.floor(USERONLINE/60);
1371 var m = USERONLINE-h*60;
1372 m<10 ? m="0"+m : '';
1373 onlinetime.html(h+":"+m);
1374}
1375
1376// set user CSS
1377
1378function setUserCSS() {
1379 $("#chanexternalcss").detach().appendTo("head");
1380 $("#chanexternalcss-fix").remove();
1381
1382 cssfix = '#mainpage {padding-top:52px}\n'
1383 + '#motdrow, #announcements, #main, #playlistrow {border:solid 2px transparent; margin-bottom:5px}\n'
1384 + '#main > div, #playlistrow > div {\n'
1385 + ' padding-left:5px; padding-right:5px; margin-top:5px; margin-bottom:5px;\n'
1386 + '}\n'
1387 + '#motdwrap {margin:5px -10px}\n'
1388 + '#announcements .alert {margin:0px -10px 5px}\n'
1389 + '#drinkcount {margin:0px}\n';
1390
1391 $("head").append('<style id="chanexternalcss-fix" type="text/css">'+cssfix+'</style>');
1392 $("#usertheme").attr('href', '/css/themes/slate.css');
1393 $("#usertheme-fix").remove();
1394 if (USERTHEME.indexOf("/css/themes/")>-1) {
1395 $("#usertheme").attr('href', USERTHEME);
1396 } else {
1397 $('<link id="usertheme-fix" rel="stylesheet" type="text/css" href="'+USERTHEME+'"></link>')
1398 .appendTo("head");
1399 }
1400 $("#usercss").remove();
1401 if (USERCONFIG.css!="no") {
1402 $("head").append('<style id="usercss" type="text/css">'+USERCONFIG.csscode+'</style>');
1403 }
1404}
1405
1406/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1407
1408/* ----- UI events functions ----- */
1409
1410/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1411
1412function prepareFilters() {
1413 str='{"name":"white color","source":"\\\\[white\\\\]","flags":"g",'
1414 + '"replace":"<span style=\\"color:white\\">","active":true,"filterlinks":false},'
1415 + '{"name":"yellow color","source":"\\\\[yellow\\\\]","flags":"g",'
1416 + '"replace":"<span style=\\"color:gold\\">","active":true,"filterlinks":false},'
1417 + '{"name":"orange color","source":"\\\\[orange\\\\]","flags":"g",'
1418 + '"replace":"<span style=\\"color:orange\\">","active":true,"filterlinks":false},'
1419 + '{"name":"pink color","source":"\\\\[pink\\\\]","flags":"g",'
1420 + '"replace":"<span style=\\"color:#FFBBFF\\">","active":true,"filterlinks":false},'
1421 + '{"name":"red color","source":"\\\\[red\\\\]","flags":"g",'
1422 + '"replace":"<span style=\\"color:red\\">","active":true,"filterlinks":false},'
1423 + '{"name":"limegreen color","source":"\\\\[lime\\\\]","flags":"g",'
1424 + '"replace":"<span style=\\"color:limegreen\\">","active":true,"filterlinks":false},'
1425 + '{"name":"green color","source":"\\\\[green\\\\]","flags":"g",'
1426 + '"replace":"<span style=\\"color:green\\">","active":true,"filterlinks":false},'
1427 + '{"name":"aqua color","source":"\\\\[aqua\\\\]","flags":"g",'
1428 + '"replace":"<span style=\\"color:aqua\\">","active":true,"filterlinks":false},'
1429 + '{"name":"blue color","source":"\\\\[blue\\\\]","flags":"g",'
1430 + '"replace":"<span style=\\"color:blue\\">","active":true,"filterlinks":false},'
1431 + '{"name":"violet color","source":"\\\\[violet\\\\]","flags":"g",'
1432 + '"replace":"<span style=\\"color:#660099\\">","active":true,"filterlinks":false},'
1433 + '{"name":"brown color","source":"\\\\[brown\\\\]","flags":"g",'
1434 + '"replace":"<span style=\\"color:#660000\\">","active":true,"filterlinks":false},'
1435 + '{"name":"silver color","source":"\\\\[silver\\\\]","flags":"g",'
1436 + '"replace":"<span style=\\"color:silver\\">","active":true,"filterlinks":false},'
1437 + '{"name":"black color","source":"\\\\[black\\\\]","flags":"g",'
1438 + '"replace":"<span style=\\"color:black\\">","active":true,"filterlinks":false},'
1439 + '{"name":"white color on black","source":"\\\\[bw\\\\]","flags":"g",'
1440 + '"replace":"<span style=\\"color:white; background-color:black\\">","active":true,"filterlinks":false},'
1441 + '{"name":"bold text","source":"\\\\[b\\\\]","flags":"g",'
1442 + '"replace":"<span style=\\"font-weight:bold\\">","active":true,"filterlinks":false},'
1443 + '{"name":"italic text","source":"\\\\[i\\\\]","flags":"g",'
1444 + '"replace":"<span style=\\"font-style:italic\\">","active":true,"filterlinks":false},'
1445 + '{"name":"underlined text","source":"\\\\[u\\\\]","flags":"g",'
1446 + '"replace":"<span style=\\"text-decoration:underline\\">","active":true,"filterlinks":false},'
1447 + '{"name":"striked text","source":"\\\\[s\\\\]","flags":"g",'
1448 + '"replace":"<span style=\\"text-decoration:line-through\\">","active":true,"filterlinks":false},'
1449 + '{"name":"distinguished text","source":"\\\\[d\\\\]","flags":"g",'
1450 + '"replace":"<span class=\\"dist\\">","active":true,"filterlinks":false},'
1451 + '{"name":"fire text","source":"\\\\[f\\\\]","flags":"g",'
1452 + '"replace":"<span style=\\"color:#FFFFFF; font-family:impact, sans-serif; padding-top:20px; '
1453 + 'text-shadow:0px 0px 4px #000000, 0px -5px 4px #FFFF33, 2px -8px 6px #FFDD33, -2px -15px 10px #FF8800, '
1454 + '2px -20px 18px #FF2200; letter-spacing:2px\\">","active":true,"filterlinks":false},'
1455 + '{"name":"short spoiler","source":"\\\\[sp\\\\]","flags":"g",'
1456 + '"replace":"<span class=\\"spoiler\\">","active":true,"filterlinks":false},'
1457 + '{"name":"closing font style","source":"\\\\[\\\\/\\\\]","flags":"g",'
1458 + '"replace":"</span>","active":true,"filterlinks":false}]';
1459
1460 callback = function(data) {
1461 socket.listeners("chatFilters").splice(
1462 socket.listeners("chatFilters").indexOf(callback)
1463 );
1464 json=JSON.stringify(data);
1465 comma = (json.length!="2") ? ',' : '';
1466 $("#cs-chatfilters-exporttext").val(json.substring(0, json.length-1)+comma+str);
1467 };
1468
1469 socket.on("chatFilters", callback);
1470 socket.emit("requestChatFilters");
1471
1472 txt = 'This option does NOT affect your current filters, all of them will be saved.\n'
1473 + 'Click "Import filter list" button if you\'ll decide to install.';
1474 alert(txt);
1475}
1476
1477// show chat additional functions
1478
1479function showChatFunctions() {
1480 $("#userlist").append('<div id="chatfunc-dropdown" />');
1481 setPanelProperties("#chatfunc-dropdown");
1482
1483 $("#chatfunc-dropdown").append('<div>Prevent room freezing if spam:</div>');
1484
1485 spamclearbtn = $('<button id="spamclear-btn" class="btn btn-xs btn-default">Auto Clear</button>')
1486 .appendTo("#chatfunc-dropdown")
1487 .on("click", function() {
1488 if (!CLEARING) {
1489 $(this).text('Stop Clearing').addClass('btn-danger');
1490 CLEARING=setInterval(function() {
1491 socket.emit("chatMsg", {msg: '/clear'});
1492 }, 500);
1493 } else {
1494 $(this).text('Auto Clear').removeClass('btn-danger');
1495 clearInterval(CLEARING);
1496 CLEARING=false;
1497 }
1498 });
1499 CLEARING ? $("#spamclear-btn").text('Stop Clearing').addClass('btn-danger') : '';
1500
1501 $("#chatfunc-dropdown").append('<div>Prevent me from AFK:</div>');
1502
1503 antiafkbtn = $('<button id="antiafk-btn" class="btn btn-xs btn-default">Anti AFK</button>')
1504 .appendTo("#chatfunc-dropdown")
1505 .on("click", function() {
1506 if (!ANTIAFK) {
1507 $(this).addClass('btn-danger');
1508 ANTIAFK=setInterval(function() {
1509 $("#userlist").find('span[class^=userlist]').each(function() {
1510 if ($(this).html()==CLIENT.name && $(this).css('font-style')=="italic") {
1511 socket.emit("chatMsg", {msg: '/afk'});
1512 return;
1513 }
1514 });
1515 }, 4000);
1516 } else {
1517 $(this).removeClass('btn-danger');
1518 clearInterval(ANTIAFK);
1519 ANTIAFK=false;
1520 }
1521 });
1522 ANTIAFK ? $("#antiafk-btn").addClass('btn-danger') : '';
1523}
1524
1525// display list of emotes
1526
1527function showEmotes() {
1528 if (typeof GroupEmotes_Number!=="number" || GroupEmotes_Number<1) {
1529 GroupEmotes_Number=100;
1530 }
1531 len=CHANNEL.emotes.length;
1532 if (len<1) {
1533 emotespanel.addClass('row');
1534 makeAlert("No emotes available", "Ask channel administrator.").appendTo(emotespanel);
1535 } else if (UI_GroupEmotes!="1" || len<=GroupEmotes_Number) {
1536 for (i in CHANNEL.emotes) {
1537 $('<img onclick="insertText(\''+CHANNEL.emotes[i].name+' \')" />')
1538 .attr({'src':CHANNEL.emotes[i].image, 'title':CHANNEL.emotes[i].name})
1539 .appendTo(emotespanel);
1540 }
1541 } else {
1542 var arr = new Array();
1543 stop=GroupEmotes_Number-1;
1544 gr=Math.ceil(CHANNEL.emotes.length/GroupEmotes_Number);
1545 html='';
1546
1547 for (i=0; i<len; i++) {
1548 html += '<img src="'+CHANNEL.emotes[i].image+'" '
1549 + 'onclick="insertText(\''+CHANNEL.emotes[i].name+' \')" />';
1550 if (i%GroupEmotes_Number==stop) {
1551 arr.push(html);
1552 html='';
1553 }
1554 }
1555 len%GroupEmotes_Number!=0 ? arr.push(html) : '';
1556
1557 for (i=0; i<gr; i++) {
1558 div = $('<div id="emotes-'+i+'" class="groupemotes" style="display:none" />')
1559 .html(arr[i])
1560 .appendTo(emotespanel);
1561 }
1562 arr='';
1563
1564 emotesbtnwrap = $('<div id="emotesbtnwrap" />').appendTo(emotespanel);
1565 emotesbtngroup = $('<div id="emotescontrols" class="btn-group">').appendTo(emotesbtnwrap);
1566
1567 for (i=0; i<gr; i++) {
1568 btn = $('<button class="btn btn-sm btn-default emotesbtn" group="'+i+'">'+(i+1)+'</button>')
1569 .appendTo(emotesbtngroup)
1570 .on("click", function() {
1571 $(".emotesbtn").removeClass('active');
1572 $(this).addClass('active');
1573 $(".groupemotes").hide();
1574 nr=$(this).attr('group');
1575 $("#emotes-"+nr).show();
1576 });
1577 }
1578 $("#emotes-0").show();
1579 $("#emotescontrols button:nth-child(1)").addClass('active');
1580 }
1581 EMOTES=true;
1582}
1583
1584// show chat commands modal window
1585
1586function showChatHelp() {
1587 createModal('Chat Commands');
1588 if (UI_FontsBtn=="1") {
1589 body.append('<strong>Fonts commands</strong><br /><br />');
1590 html='<li><code>[white]</code>, <code>[yellow]</code>, <code>[orange]</code>, <code>[pink]</code>, '
1591 + '<code>[red]</code>, <code>[lime]</code>, <code>[green]</code>, <code>[aqua]</code>, '
1592 + '<code>[blue]</code>, <code>[violet]</code>, <code>[brown]</code>, <code>[silver]</code>, '
1593 + '<code>[black]</code> - begin of colored text</li>'
1594 + '<li><code>[bw]</code> - begin of white text on the black background</li>'
1595 + '<li><code>[b]</code>, <code>[i]</code>, <code>[u]</code>, <code>[s]</code> - '
1596 + 'begin of bold, italic, underlined, striked or underlined text</li>'
1597 + '<li><code>[d]</code> - begin of a distinguished text (red on yelllow background)</li>'
1598 + '<li><code>[f]</code> - begin of a text with a fire effect</li>'
1599 + '<li><code>[sp]</code> - begin of an inline spoiler</li>'
1600 + '<li><code>[/]</code> - <b>end of any color, style or spoiler</b></li><br />'
1601 + 'If fonts commands don\'t work, ask script administrator about proper filters installation.';
1602 $('<ul />').html(html).appendTo(body);
1603 }
1604 if (UI_UserCommands=="1") {
1605 arr = {
1606 'pick':'choosing a random option from a list separated by commas '
1607 + '(e.g. <i>!pick japan,korea,china</i>)',
1608 'ask':'asking a question with yes/no type answer '
1609 + '(e.g. <i>!ask Will this channel be popular?</i>)',
1610 'q':'displaying random quote (<i>!q</i>)',
1611 'dice':'rolling dice (<i>!dice</i>)',
1612 'roll':'rolling 3-digit number (<i>!roll</i>)',
1613 'time':'displaying current time (<i>!time</i>)',
1614 'now':'displaying current playing title (<i>!now</i>)',
1615 'calc':'calculating a math operation '
1616 + '(all JavaScript Math methods and constants allowed, e.g. <i>!calc Math.PI*10</i>)',
1617 'skip':'skip current item (<i>!skip</i>)',
1618 'add':'adding a link to the end of playlist '
1619 + '(e.g. <i>!add https://www.youtube.com/watch?v=29FFHC2D12Q</i>)',
1620 'stat': 'displaying user chat statistics in current session (<i>!stat</i>)',
1621 'memestats': 'displaying number memes used by user in all messages (<i>!memestats</i>)'
1622 }
1623 if (UI_ChannelDatabase=="1") {
1624 arr['random']='adding random link from database (<i>!random</i>)';
1625 }
1626 body.append('<strong>New chat commands</strong><br /><br />');
1627 ul = $('<ul />').appendTo(body);
1628 for (cmd in arr) {
1629 ul.append('<li><code>!'+cmd+'</code> - '+arr[cmd]+'</li>');
1630 }
1631 }
1632 if (UI_ChatSpeak=="1") {
1633 body.append('<strong>Voice commands</strong><br /><br />');
1634 html='<li><code>!say</code> - text speaking in english (<i>!say Hello!</i>)</li>'
1635 + '<li><code>!mow</code> - text speaking in polish (<i>!mow Chrząszcz brzmi w trzcinie.</i>)';
1636 $('<ul />').html(html).appendTo(body);
1637 }
1638 arr = {
1639 'me':'showing an action-style message (username does something, e.g. <i>/me is dancing</i>)',
1640 'sp':'hiding a message in a hover-to-show spoiler box (e.g. <i>/sp This message is hidden</i>)',
1641 'afk':'toggling your AFK (away from keyboard) status (<i>/afk</i>)',
1642 }
1643 body.append('<br /><strong>Default CyTube commands</strong><br /><br />');
1644 ul = $('<ul />').appendTo(body);
1645 for (cmd in arr) {
1646 ul.append('<li><code>/'+cmd+'</code> - '+arr[cmd]+'</li>');
1647 }
1648}
1649
1650// show chat sounds panel
1651
1652function showSoundsPanel() {
1653 $("#userlist").append('<div id="sounds-dropdown" />');
1654 setPanelProperties("#sounds-dropdown");
1655
1656 muteallbtn = $('<button id="muteall-btn" class="btn btn-xs btn-default">Mute All</button>')
1657 .appendTo("#sounds-dropdown")
1658 .on("click", function() {
1659 if (VOICES) {
1660 $(this).text('Unmute All').addClass('btn-danger');
1661 voicesbtn.addClass('btn-danger').attr('title', 'Unmute chat voices');
1662 VOICES = false;
1663 SOUNDSPANEL = false;
1664 $("#sounds-dropdown").remove();
1665 } else {
1666 $(this).text('Mute All').removeClass('btn-danger');
1667 voicesbtn.removeClass('btn-danger').attr('title', 'Mute chat voices');
1668 VOICES = true;
1669 }
1670 });
1671 !VOICES ? muteallbtn.text('Unmute All').addClass('btn-danger') : '';
1672
1673 $("#sounds-dropdown").append('<div>Sounds level:</div>');
1674
1675 lvlgroup = $('<div id="lvlgroup" class="btn-group"></div>')
1676 .appendTo("#sounds-dropdown");
1677
1678 for (i=1; i<=5; i++) {
1679 btn=$('<button class="btn btn-xs btn-default" id="lvlvol'+i+'" level="'+i+'" />')
1680 .html(i)
1681 .appendTo(lvlgroup)
1682 .on("click", function() {
1683 $("#lvlvol"+SOUNDSLVL).removeClass('btn-success');
1684 SOUNDSLVL=$(this).attr('level');
1685 setOpt(CHANNEL.name+"_soundslvl", SOUNDSLVL);
1686 $(this).addClass('btn-success');
1687 });
1688 }
1689 $("#lvlvol"+SOUNDSLVL).addClass('btn-success');
1690
1691 $("#sounds-dropdown").append('<div>Select users to mute sounds:</div>');
1692
1693 mutegroup = $('<div id="mutegroup" class="btn-group-vertical"></div>').appendTo("#sounds-dropdown");
1694
1695 $(".userlist_item").each(function() {
1696 user=$(this).find("span:nth-child(2)").html();
1697 btn=$('<button class="btn btn-xs btn-default" name="'+user+'" />')
1698 .html(user)
1699 .appendTo(mutegroup)
1700 .on("click", function() {
1701 name=$(this).attr('name');
1702 if (name in MUTEDVOICES && MUTEDVOICES[name]=="1") {
1703 $(this).removeClass('btn-danger');
1704 MUTEDVOICES[name]=0;
1705 } else {
1706 $(this).addClass('btn-danger');
1707 MUTEDVOICES[name]=1;
1708 }
1709 });
1710 (user in MUTEDVOICES && MUTEDVOICES[user]=="1") ? btn.addClass('btn-danger') : '';
1711 });
1712}
1713
1714// show moderators panel
1715
1716function showModPanel() {
1717 createModal("Moderators panel");
1718
1719 html='';
1720 for (i in ModPanel_Array) {
1721 name=ModPanel_Array[i][0];
1722 mess=ModPanel_Array[i][1];
1723 if (name=="") {
1724 html+='<i class="glyphicon glyphicon-comment"></i> '
1725 + mess
1726 + '<br /><br />';
1727 } else if (name==CLIENT.name) {
1728 html+='<i class="glyphicon glyphicon-comment"></i> '
1729 + '<i class="glyphicon glyphicon-user"></i> '
1730 + '(to: '+CLIENT.name+') → '+mess
1731 + '<br /><br />';
1732 }
1733 }
1734 body.append(html);
1735 $("#mod-btn").removeClass('btn-danger').html('<i class="glyphicon glyphicon-tasks"></i>');
1736 setOpt(CHANNEL.name+"_modhash", HASH);
1737}
1738
1739// show info about current or next media
1740
1741function showInfo() {
1742 if (DEFDESCR) {
1743 contr=$(".queue_active").attr("title");
1744 if (typeof contr==="undefined") {
1745 text='Nothing Playing';
1746 } else {
1747 duration=$(".queue_active .qe_time").html();
1748 text=contr+' ['+duration+']';
1749 }
1750 mediainfo.html(text);
1751 } else {
1752 var arr=new Array();
1753 text='Playing Next:';
1754 li1=$(".queue_active").next();
1755 li2=li1.next();
1756 li3=li2.next();
1757 li1.length>0 ? arr.push(' 1▸ '+li1.children("a").html()) : '';
1758 li2.length>0 ? arr.push(' // 2▸ '+li2.children("a").html()) : '';
1759 li3.length>0 ? arr.push(' // 3▸ '+li3.children("a").html()) : '';
1760 text+=arr.join("");
1761 arr.length<3 ? text+=' // END OF PLAYLIST //' : '';
1762 mediainfo.html('<marquee scrollamount="5">'+text+'</marquee>');
1763 }
1764}
1765
1766// hide and show player with covering image
1767
1768function coverPlayer() {
1769 PlayerHiding_URL=="" ? PlayerHiding_URL='https://dl.dropboxusercontent.com/s/xz2o99scw5i7aai/stop.png' : '';
1770 $("#videowrap").addClass('relative');
1771 w=$("#ytapiplayer").css('width');
1772 h=$("#videowrap").css('height').replace('px', '')-31;
1773 coverpl = $('<div id="coverpl" />')
1774 .css({'width':w, 'height':h+'px', 'background-image':'url('+PlayerHiding_URL+')'})
1775 .appendTo($("#videowrap"));
1776 hideplayerbtn.addClass('btn-danger').attr('title', 'Show player');
1777}
1778
1779function showPlayer() {
1780 coverpl.remove();
1781 hideplayerbtn.removeClass('btn-danger').attr('title', 'Hide player');
1782 $("#videowrap").removeClass('relative');
1783}
1784
1785// mute YT player
1786
1787function mutePlayer() {
1788 (PLAYER && PLAYER.type=="yt") ? PLAYER.player.mute() : '';
1789}
1790
1791// unmute YT player
1792
1793function unmutePlayer() {
1794 (PLAYER && PLAYER.type=="yt") ? PLAYER.player.unMute() : '';
1795}
1796
1797// download current item
1798
1799function downloadCurrentItem() {
1800 uid=$(".pluid-"+PL_CURRENT).data("media");
1801 arr={
1802 'yt':'http://youtube.com/watch?v=',
1803 'vi':'http://vimeo.com/',
1804 'dm':'http://dailymotion.com/video/',
1805 'sc':''
1806 }
1807 link = (uid.type in arr ? arr[uid.type]+''+uid.id : '');
1808 if (link=="") {
1809 alert('This link is not supported. Try YouTube, Vimeo, Dailymotion or SoundCloud.');
1810 }
1811 else {
1812 createModal("Download current item");
1813
1814 $('<a href="http://keepvid.com/?url='+link+'" target="_blank">Click here to download</a>')
1815 .appendTo(body)
1816 .on("click", function() {
1817 outer.modal('hide');
1818 });
1819 }
1820}
1821
1822// preview YT video in modal window
1823
1824function prevVideo(a) {
1825 createModal('Preview Video');
1826
1827 player=$('<iframe id="previewFrame" width="558" height="314" frameborder="0" />')
1828 .attr('src', 'http://www.youtube.com/embed/'+a+'?wmode=transparent&enablejsapi')
1829 .appendTo(body);
1830}
1831
1832// toggle configuration panel
1833
1834function toggleConfigPanel() {
1835 if (MINIMIZED) {
1836 $("#rightpane-inner").show();
1837 $("#azukirow, #leftpane-inner").show();
1838 !$("#hide-motd").prop('checked') ? $("#motdrow").show() : '';
1839 !$("#hide-ann").prop('checked') ? $("#announcements").show() : '';
1840 !$("#hide-hf").prop('checked') ? $("footer").show() : '';
1841 pinupbtn.show();
1842 layoutbtn.removeClass('btn-danger').addClass('btn-success')
1843 .html('<span class="glyphicon glyphicon-cog"></span> Layout');
1844 $("#min-layout").prop('checked', false);
1845 $("#plcontrol button, #db-btn, #gallery-btn, #newpollbtn").removeAttr('disabled');
1846 MINIMIZED=false;
1847 // patch: giving back 15px additional space on the top
1848 $("#mainpage").css('margin-top', $("#mainpage").css('margin-top').replace('px', '')*1-15+'px');
1849 } else {
1850 toggleDiv(configwrap);
1851 if (configwrap.css('display')=="none") {
1852 layoutbtn.removeClass('btn-success');
1853 } else {
1854 layoutbtn.addClass('btn-success');
1855 }
1856 LAYOUTBOX = !LAYOUTBOX;
1857 setOpt(CHANNEL.name+"_layoutbox", LAYOUTBOX);
1858 }
1859}
1860
1861// show layout configuration modal window
1862
1863function showConfig() {
1864 createModal("Layout Configuration");
1865
1866 form = $('<form class="form-horizontal" />').appendTo(body);
1867
1868 function addOption(txt, elem) {
1869 g = $('<div class="form-group" />').appendTo(form);
1870 $('<label class="control-label col-sm-4" />').text(txt).appendTo(g);
1871 c = $('<div class="col-sm-8" />').appendTo(g);
1872 elem.appendTo(c);
1873 }
1874
1875 playerlocation = $('<select />').addClass('form-control');
1876 $('<option />').attr('value', 'left').text('left').appendTo(playerlocation);
1877 $('<option />').attr('value', 'right').text('right').appendTo(playerlocation);
1878 $('<option />').attr('value', 'center').text('center').appendTo(playerlocation);
1879 playerlocation.val(USERCONFIG.player);
1880 addOption('Player location', playerlocation);
1881
1882 userlistlocation = $('<select />').addClass('form-control');
1883 $('<option />').attr('value', 'left').text('left').appendTo(userlistlocation);
1884 $('<option />').attr('value', 'right').text('right').appendTo(userlistlocation);
1885 userlistlocation.val(USERCONFIG.userlist);
1886 addOption('Userlist location', userlistlocation);
1887
1888 queuelocation = $('<select />').addClass('form-control');
1889 $('<option />').attr('value', 'left').text('left').appendTo(queuelocation);
1890 $('<option />').attr('value', 'right').text('right').appendTo(queuelocation);
1891 $('<option />').attr('value', 'center').text('center').appendTo(queuelocation);
1892 queuelocation.val(USERCONFIG.queue);
1893 addOption('Queue location', queuelocation);
1894
1895 queuesize = $('<select />').addClass('form-control');
1896 $('<option />').attr('value', 'wide').text('wide').appendTo(queuesize);
1897 $('<option />').attr('value', 'narrow').text('narrow').appendTo(queuesize);
1898 queuesize.val(USERCONFIG.qsize);
1899 addOption('Queue column size', queuesize);
1900
1901 mainlocation = $('<select />').addClass('form-control');
1902 $('<option />').attr('value', 'top').text('above playlist').appendTo(mainlocation);
1903 $('<option />').attr('value', 'bottom').text('below playlist').appendTo(mainlocation);
1904 mainlocation.val(USERCONFIG.main);
1905 addOption('Player & chat', mainlocation);
1906
1907 motdlocation = $('<select />').addClass('form-control');
1908 $('<option />').attr('value', 'top').text('channel top').appendTo(motdlocation);
1909 $('<option />').attr('value', 'bottom').text('channel bottom').appendTo(motdlocation);
1910 motdlocation.val(USERCONFIG.motd);
1911 addOption('MOTD & announcements', motdlocation);
1912
1913 logoinsert = $('<select />').addClass('form-control');
1914 $('<option />').attr('value', 'no').text('no image').appendTo(logoinsert);
1915 $('<option />').attr('value', 'user').text('user image').appendTo(logoinsert);
1916 for (i in TopUserLogo) {
1917 $("<option />").attr('value', i).text(TopUserLogo[i][0]).appendTo(logoinsert);
1918 }
1919 logoinsert.val(USERCONFIG.logo);
1920 addOption('Top logo', logoinsert);
1921
1922 userlogo = $('<input />').addClass('form-control').attr('type', 'text')
1923 .attr('placeholder', 'Image URL');
1924 userlogo.val('');
1925 addOption('User logo URL', userlogo);
1926
1927 userlogoht = $('<input />').addClass('form-control').attr('type', 'text')
1928 .attr('placeholder', 'Image Height (in px)');
1929 userlogoht.val('');
1930 addOption('User logo height', userlogoht);
1931
1932 if (USERCONFIG.logo!="user") {
1933 userlogo.parent().parent().hide();
1934 userlogoht.parent().parent().hide();
1935 } else {
1936 userlogo.val(USERCONFIG.logourl);
1937 userlogoht.val(USERCONFIG.logoht);
1938 }
1939
1940 headermode = $('<select />').addClass('form-control')
1941 $('<option />').attr('value', 'fixed').text('fixed').appendTo(headermode);
1942 $('<option />').attr('value', 'detached').text('detached').appendTo(headermode);
1943 $('<option />').attr('value', 'mouseover').text('mouseover').appendTo(headermode);
1944 headermode.val(USERCONFIG.header);
1945 addOption('Header menu', headermode);
1946
1947 customcss = $('<select />').addClass('form-control');
1948 $('<option />').attr('value', 'no').text('no').appendTo(customcss);
1949 $('<option />').attr('value', 'yes').text('yes').appendTo(customcss);
1950 customcss.val(USERCONFIG.css);
1951 addOption('Custom CSS', customcss);
1952
1953 usercss = $('<textarea rows="8" />').addClass('form-control')
1954 .attr('placeholder', 'Insert CSS code');
1955 usercss.val(USERCONFIG.csscode);
1956 addOption('CSS code', usercss);
1957
1958 if (USERCONFIG.css=="no") {
1959 usercss.parent().parent().hide();
1960 }
1961
1962 submit = $('<button class="btn btn-default btn-success" />').text("Save changes").appendTo(footer);
1963 reset = $('<button class="btn btn-default pull-left" />').text('Default').appendTo(footer);
1964 column = $('<button class="btn btn-default pull-left" />').text('One column').appendTo(footer);
1965
1966 logoinsert.on("change", function() {
1967 if (logoinsert.val()=="user") {
1968 userlogo.parent().parent().show();
1969 userlogoht.parent().parent().show();
1970 userlogo.val(USERCONFIG.logourl);
1971 userlogoht.val(USERCONFIG.logoht);
1972 } else {
1973 userlogo.parent().parent().hide();
1974 userlogoht.parent().parent().hide();
1975 }
1976 });
1977
1978 customcss.on("change", function() {
1979 if (customcss.val()=="yes") {
1980 usercss.parent().parent().show();
1981 } else {
1982 usercss.parent().parent().hide();
1983 }
1984 });
1985
1986 submit.on("click", function() {
1987 outer.modal('hide');
1988
1989 USERCONFIG.player=playerlocation.val();
1990 setOpt(CHANNEL.name+"_player",playerlocation.val());
1991
1992 USERCONFIG.userlist=userlistlocation.val();
1993 setOpt(CHANNEL.name+"_userlist",userlistlocation.val());
1994
1995 USERCONFIG.queue=queuelocation.val();
1996 setOpt(CHANNEL.name+"_queue",queuelocation.val());
1997
1998 USERCONFIG.qsize=queuesize.val();
1999 setOpt(CHANNEL.name+"_qsize",queuesize.val());
2000
2001 USERCONFIG.main=mainlocation.val();
2002 setOpt(CHANNEL.name+"_main",mainlocation.val());
2003
2004 USERCONFIG.motd=motdlocation.val();
2005 setOpt(CHANNEL.name+"_motd",motdlocation.val());
2006
2007 if (logoinsert.val()=="user") {
2008 if (userlogo.val()=="") {
2009 logoinsert.val("no");
2010 } else if (userlogoht.val()=="") {
2011 userlogoht.val('200');
2012 } else {
2013 a=userlogoht.val()*1;
2014 if (isNaN(a) || a<1) {
2015 userlogoht.val('200');
2016 }
2017 }
2018 USERCONFIG.logourl=userlogo.val();
2019 USERCONFIG.logoht=userlogoht.val();
2020 setOpt(CHANNEL.name+"_logourl",userlogo.val());
2021 setOpt(CHANNEL.name+"_logoht",userlogoht.val());
2022 }
2023
2024 USERCONFIG.logo=logoinsert.val();
2025 setOpt(CHANNEL.name+"_logo",logoinsert.val());
2026
2027 USERCONFIG.header=headermode.val();
2028 setOpt(CHANNEL.name+"_header",headermode.val());
2029
2030 if (customcss.val()=="yes") {
2031 USERCONFIG.csscode=usercss.val();
2032 setOpt(CHANNEL.name+"_csscode",usercss.val());
2033 }
2034
2035 USERCONFIG.css=customcss.val();
2036 setOpt(CHANNEL.name+"_css",customcss.val());
2037
2038 setLayout();
2039 scrollChat();
2040 scrollQueue();
2041 });
2042
2043 reset.on("click", function() {
2044 outer.modal("hide");
2045
2046 USERCONFIG.player=defplayer;
2047 setOpt(CHANNEL.name+"_player",defplayer);
2048
2049 USERCONFIG.userlist=defuserlist;
2050 setOpt(CHANNEL.name+"_userlist",defuserlist);
2051
2052 USERCONFIG.queue=defqueue;
2053 setOpt(CHANNEL.name+"_queue",defqueue);
2054
2055 USERCONFIG.qsize="wide";
2056 setOpt(CHANNEL.name+"_qsize","wide");
2057
2058 USERCONFIG.main="top";
2059 setOpt(CHANNEL.name+"_main","top");
2060
2061 USERCONFIG.motd="top";
2062 setOpt(CHANNEL.name+"_motd","top");
2063
2064 USERCONFIG.logo="no";
2065 setOpt(CHANNEL.name+"_logo","no");
2066
2067 USERCONFIG.header="detached";
2068 setOpt(CHANNEL.name+"_header","detached");
2069
2070 USERCONFIG.css="no";
2071 setOpt(CHANNEL.name+"_css","no");
2072
2073 setLayout();
2074 scrollChat();
2075 scrollQueue();
2076 });
2077
2078 column.on("click", function() {
2079 outer.modal("hide");
2080
2081 USERCONFIG.player="center";
2082 setOpt(CHANNEL.name+"_player","center");
2083
2084 USERCONFIG.userlist="left";
2085 setOpt(CHANNEL.name+"_userlist","left");
2086
2087 USERCONFIG.queue="center";
2088 setOpt(CHANNEL.name+"_queue","center");
2089
2090 USERCONFIG.main="top";
2091 setOpt(CHANNEL.name+"_main","top");
2092
2093 USERCONFIG.motd="bottom";
2094 setOpt(CHANNEL.name+"_motd","bottom");
2095
2096 USERCONFIG.logo="no";
2097 setOpt(CHANNEL.name+"_logo","no");
2098
2099 USERCONFIG.header="detached";
2100 setOpt(CHANNEL.name+"_header","detached");
2101
2102 setLayout();
2103 scrollChat();
2104 scrollQueue();
2105 });
2106}
2107
2108// toggle fluid layout
2109
2110function toggleFluidLayout() {
2111 if (FLUID) {
2112 $("body").removeClass('fullscreen');
2113 $(".container-fluid").removeClass('container-fluid').addClass('container');
2114 $("#fontspanel, #emotespanel").removeClass('fluidpanel');
2115 } else {
2116 $("body").addClass('fullscreen');
2117 $(".container").removeClass('container').addClass('container-fluid');
2118 $("footer .container-fluid").removeClass('container-fluid').addClass('container');
2119 $("#fontspanel, #emotespanel").addClass('fluidpanel');
2120 }
2121 UI_DisplayModeSel=="1" ? setMode(modesel.val()) : '';
2122 FLUID=!FLUID;
2123 setOpt(CHANNEL.name+"_fluid", FLUID);
2124 scrollChat();
2125}
2126
2127// toggle minimized layout
2128
2129function toggleMinLayout() {
2130 if (!MINIMIZED) {
2131 $("#rightpane-inner").hide();
2132 $("#azukirow, #motdrow, #announcements, #leftpane-inner, footer").hide();
2133 pinupbtn.hide();
2134 layoutbtn.removeClass('btn-success').addClass('btn-danger').html('Maximize');
2135 $("#plcontrol button, #db-btn, #gallery-btn, #newpollbtn").attr('disabled', 'disabled');
2136 MINIMIZED=true;
2137 // patch for 15px more space on the top
2138 $("#mainpage").css('margin-top', $("#mainpage").css('margin-top').replace('px', '')*1+15+'px');
2139 }
2140}
2141
2142// pin-up playlist to player
2143
2144function pinUp() {
2145 if (USERCONFIG.player=="left") {
2146 $("#videowrap").after($("#rightpane").detach());
2147 } else if (USERCONFIG.player=="right") {
2148 $("#videowrap").before($("#rightpane").detach());
2149 }
2150 if (USERCONFIG.queue=="left") {
2151 $("#leftpane").before($("#chatwrap").detach());
2152 } else if (USERCONFIG.queue=="right") {
2153 $("#leftpane").after($("#chatwrap").detach());
2154 }
2155 $("#rightpane").removeClass().addClass('col-lg-5 col-md-5');
2156 if (USERCONFIG.qsize=="wide") {
2157 $("#chatwrap").removeClass().addClass('col-lg-7 col-md-7');
2158 } else {
2159 $("#chatwrap").removeClass().addClass('col-lg-5 col-md-5');
2160 }
2161 $("#pinup-btn").attr('title', 'Unpin playlist');
2162 $("#config-btn, #configbtnwrap br").hide();
2163 $("#min-layout").parent().hide();
2164 $("#mode-sel").find("option[value='chMode'], option[value='sMode']").hide();
2165 PINNED=true;
2166}
2167
2168// un-pin playlist from player
2169
2170function unPin() {
2171 if (USERCONFIG.queue=="left") {
2172 $("#leftpane").before($("#rightpane").detach());
2173 } else if (USERCONFIG.queue=="right") {
2174 $("#leftpane").after($("#rightpane").detach());
2175 }
2176 if (USERCONFIG.player=="left") {
2177 $("#videowrap").after($("#chatwrap").detach());
2178 } else if (USERCONFIG.player=="right") {
2179 $("#videowrap").before($("#chatwrap").detach());
2180 }
2181 $("#chatwrap").removeClass().addClass('col-lg-5 col-md-5');
2182 if (USERCONFIG.qsize=="wide") {
2183 $("#rightpane").removeClass().addClass('col-lg-7 col-md-7');
2184 } else {
2185 $("#rightpane").removeClass().addClass('col-lg-5 col-md-5');
2186 }
2187 $("#pinup-btn").attr('title', 'Pinup playlist to player');
2188 $("#config-btn, #configbtnwrap br").show();
2189 $("#min-layout").parent().show();
2190 $("#mode-sel").find("option[value='chMode'], option[value='sMode']").show();
2191 PINNED=false;
2192}
2193
2194// show contributors list
2195
2196function showContributors() {
2197 createModal("Contributors List");
2198
2199 len=$("#queue li").length+1;
2200 var list = [];
2201 for (i=1; i<len; i++) {
2202 item=$("#queue li:nth-child("+i+")").attr('title');
2203 user=item.split("by: ")[1];
2204 user in list ? list[user]++ : list[user]=1;
2205 }
2206 var list2 = [];
2207 for (key in list) {
2208 list2.push([key, list[key]]);
2209 }
2210 list2.sort(function(a,b) {return a[1]-b[1]});
2211 list2.reverse();
2212 var list3 = [];
2213 for (i in list2) {
2214 list3.push((i*1+1)+". "+list2[i].join(": "));
2215 }
2216 html='<strong>Number of added playlist items:</strong>'
2217 + '<br /><br />'+list3.join("<br />");
2218 body.append(html);
2219}
2220
2221// expand/collapse queue
2222
2223function expandQueue() {
2224 if (!FULLPL) {
2225 $("#queue").css('max-height', '100000px');
2226 expandbtn.attr('title', 'Collapse playlist');
2227 FULLPL=true;
2228 } else {
2229 $("#queue").css('max-height', '500px');
2230 expandbtn.attr('title', 'Expand playlist');
2231 FULLPL=false;
2232 scrollQueue();
2233 }
2234}
2235
2236// get playlist URLS
2237
2238function getPlaylistURLs() {
2239 createModal('Playlist URLs');
2240
2241 data=$('<textarea rows="10" class="form-control" />').val(formatRawList()).appendTo(body);
2242 rlist=$('<button class="btn btn-default pull-left">Raw Links</button>').appendTo(footer);
2243 tlist=$('<button class="btn btn-default pull-left">Plain Text</button>').appendTo(footer);
2244 hlist=$('<button class="btn btn-default pull-left">HTML Code</button>').appendTo(footer);
2245 olist=$('<button class="btn btn-default pull-left">Ordered List</button>').appendTo(footer);
2246 dlist=$('<button class="btn btn-default pull-left">Database Format</button>').appendTo(footer);
2247
2248 rlist.on("click", function() {
2249 data.val(formatRawList());
2250 });
2251 tlist.on("click", function() {
2252 data.val(formatPlainTextList());
2253 });
2254 hlist.on("click", function() {
2255 data.val('<ol>\n'+formatHTMLList()+'\n</ol>');
2256 });
2257 olist.on("click", function() {
2258 data.val(formatOrderedList());
2259 });
2260 dlist.on("click", function() {
2261 data.val(formatDBList());
2262 });
2263}
2264
2265// add random item from channel database
2266
2267function addRandomItem() {
2268 time=(new Date()).getTime();
2269 if ((time-LASTADD)<120000) {
2270 alert('You can add random video every 2 minutes.');
2271 } else {
2272 var link="";
2273 while (link=="") {
2274 rnd=Math.round(Math.random()*(ChannelDatabase.length-1));
2275 link=ChannelDatabase[rnd][0];
2276 }
2277 addToPlaylist(link, "end");
2278 LASTADD=time;
2279 }
2280}
2281
2282/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2283
2284/* ----- User Interface ----- */
2285
2286/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2287
2288// adding important hidden reference row
2289
2290zerorow = $('<div id="zerorow" class="row" />').insertBefore("#motdrow");
2291
2292// adding top logo row
2293
2294azukirow = $('<div id="azukirow" class="row" />').insertBefore(zerorow);
2295
2296// adding video wrap if user has enabled "Hide Player" option
2297
2298if (USEROPTS.hidevid) {
2299 $("#chatwrap, #chatline").removeClass('col-lg-12 col-md-12').addClass('col-lg-5 col-md-5');
2300 videowrap = $('<div id="videowrap" class="col-lg-7 col-md-7" />')
2301 .insertBefore("#chatwrap");
2302 currenttitle = $('<p id="currenttitle" />')
2303 .html('Currently Playing: '+$(".queue_active a").html())
2304 .appendTo(videowrap);
2305 ytapiplayer = $('<div id="ytapiplayer" />')
2306 .appendTo(videowrap);
2307
2308 html='According to your User Preferences, video player is hidden. '
2309 + 'Click a button below to continue hiding player. '
2310 + 'Click default "Reload" icon to show player in this session. '
2311 + 'If you\'ll stay in "Chat Only" mode, functionality of this channel will be limited.<br /><br />';
2312 makeAlert("No Player", html).appendTo(ytapiplayer);
2313
2314 staybtn = $('<button id="stay-btn" class="btn btn-default btn-sm">Stay In "Chat Only" Mode</button>')
2315 .appendTo("#ytapiplayer .alert")
2316 .on("click", function() {
2317 videowrap.remove();
2318 $("#chatwrap").removeClass().addClass('col-lg-12 col-md-12');
2319 $("#configform, #modeform, #pinup-btn").hide();
2320 fitChat("auto");
2321 });
2322}
2323
2324// changing initial layout to compact for new users
2325
2326$("body").addClass('fluid');
2327compactLayout();
2328setLayout();
2329scrollChat();
2330scrollQueue();
2331
2332// adding "id" attributes
2333
2334$(".navbar-collapse .navbar-nav").children().first().attr('id', 'home-link');
2335$("#home-link").next().attr('id', 'account-link');
2336$("#account-link").next().attr('id', 'options-link');
2337$("#options-link").next().attr('id', 'channelset-link');
2338$("#channelset-link").next().attr('id', 'layout-link');
2339
2340// changing location of some layout elements
2341
2342$("#main").prepend($("#drinkbar").detach());
2343$("#videowrap").append('<div id="playercontrols" class="btn-group" />');
2344$("#playercontrols").append($("#mediarefresh").detach());
2345$("#rightpane").prepend($("#videocontrols").detach());
2346$("#rightpane").prepend($("#plcontrol").detach());
2347$("#leftpane").prepend($("#newpollbtn").detach());
2348$("#plcontrol").prepend($("#showmediaurl").detach());
2349
2350// header and footer links open in a new tab
2351
2352$("#home-link a, #account-link ul a, .credit a").attr('target', '_blank');
2353
2354// adding default CyTube Plus CSS if not set
2355
2356if ($("#chanexternalcss").length<1) {
2357 url='https://dl.dropboxusercontent.com/s/hbhlrmys5piztgo/main.css';
2358 $("head").append('<link id="chanexternalcss" href="'+url+'" rel="stylesheet" type="text/css">');
2359}
2360
2361setUserCSS();
2362
2363// adding favicon
2364
2365if (UI_Favicon=="1" && Favicon_URL!="") {
2366 $(document).ready(function() {
2367 $('<link id="chanfavicon" href="'+Favicon_URL+'" type="image/x-icon" rel="shortcut icon" />')
2368 .appendTo("head");
2369 });
2370}
2371
2372// changing carets to unicode characters
2373
2374$("nav .caret, #cs-edit-dd-toggle .caret").removeClass('caret').html(' ▾');
2375
2376// adding important messages to "Options"
2377
2378text1='Please use "Personal theme" selector in the room configuration box to select a theme for this channel. ';
2379text2='Please use "Click to configure" button in the room configuration box to configure this channel. ';
2380text3='If you want to make global changes, please go to another channel, or make changes before accepting '
2381 + 'special features.';
2382$("#us-theme").hide();
2383$("#us-theme").parent().append('<p class="text-danger">'+text1+''+text3+'</p>');
2384$("#us-layout").hide();
2385$("#us-layout").parent().append('<p class="text-danger">'+text2+''+text3+'</p>');
2386
2387// creating channel settings filters preparation button
2388
2389csfontsimport = $('<button id="cs-fonts-import" class="btn btn-default pull-right">Prepare fonts filters</button>')
2390 .insertAfter("#cs-chatfilters-import")
2391 .on("click", function() {
2392 prepareFilters();
2393 });
2394
2395// fixing layout after saving global user options
2396
2397$("#useroptions .modal-footer button:nth-child(1)").on("click", function() {
2398 USEROPTS.hidevid ? location.reload() : ''
2399 html='All changes are applying globally, but this channel uses its own layout. '
2400 + 'Please use "Click to configure" button to configure this channel layout.<br />'
2401 + 'Reload player if the wrong title is displaying. '
2402 + 'In HD layout or if player is removed, you may not see some elements due to CyTube API. '
2403 + 'If so, reload channel.';
2404 makeAlert("You have changed global User Preferences", html, "alert-info").appendTo("#announcements");
2405 compactLayout();
2406 setLayout();
2407 FLUID ? fluidLayout() : '';
2408 setUserCSS();
2409 scrollChat();
2410 scrollQueue();
2411});
2412
2413// changing channel name
2414
2415if (UI_ChannelName=="1" && ChannelName_Caption!="") {
2416 $(".navbar-brand").html(ChannelName_Caption);
2417}
2418
2419// adding channel small logo
2420
2421if (UI_MiniLogo=="1" && MiniLogo_URL!="") {
2422 $('<img id="chanavatar" src="'+MiniLogo_URL+'" />').prependTo(".navbar-brand");
2423}
2424
2425// adding header dropdown menu
2426
2427if (UI_HeaderDropMenu=="1") {
2428 HeaderDropMenu_Title=="" ? HeaderDropMenu_Title='Menu' : '';
2429 headerdrop = $('<li id="headerdrop" class="dropdown" />')
2430 .insertAfter("#home-link");
2431 $('<a class="dropdown-toggle" data-toggle="dropdown" href="#" />')
2432 .html(HeaderDropMenu_Title+' ▾')
2433 .appendTo(headerdrop);
2434 headermenu = $('<ul id="headermenu" class="dropdown-menu" />')
2435 .appendTo(headerdrop);
2436
2437 HeaderDropMenu_Array.length<1 ? HeaderDropMenu_Array=[['no menu available', '']] : '';
2438 for (i in HeaderDropMenu_Array) {
2439 title=HeaderDropMenu_Array[i][0];
2440 link=HeaderDropMenu_Array[i][1];
2441 if (link=="") {
2442 headermenu.append('<li class="dropdown-header">'+title+'</li>');
2443 } else {
2444 $('<li class="header-drop-link" />')
2445 .append('<a href="'+link+'" target="_blank">'+title+'</a>')
2446 .appendTo(headermenu);
2447 }
2448 }
2449}
2450
2451// optional removing of "Layout" menu from header
2452
2453if (UI_RemoveLayoutMenu=="1") {
2454 $("#layout-link").remove();
2455} else {
2456 $("#layout-link li:nth-child(2) a").on("click", function() {
2457 $("#configform, #modeform, #pinup-btn").hide();
2458 fitChat("auto");
2459 });
2460}
2461
2462// changing some button captions or welcome message
2463
2464if (UI_CustomCaptions=="1") {
2465 if (CustomCaptions_Array['add']!="") {
2466 $("#showmediaurl").html(CustomCaptions_Array['add']);
2467 }
2468 if (CustomCaptions_Array['refresh']!="") {
2469 $("#mediarefresh").html(CustomCaptions_Array['refresh']);
2470 }
2471 if (CustomCaptions_Array['voteskip']!="") {
2472 socket.on("voteskip", changeSkipText);
2473 changeSkipText();
2474 }
2475 if (CustomCaptions_Array['welcome']!="") {
2476 socket.on("login", changeWelcomeText);
2477 changeWelcomeText();
2478 }
2479}
2480
2481// deleting previous MOTD
2482
2483UI_MOTDDelete=="1" ? $("#motd").html('') : '';
2484
2485// setting MOTD
2486
2487if (UI_MOTDAutoLogo=="1" || UI_RulesBtn=="1" || (UI_MOTDTabs=="1" && MOTDTabs_Array.length>0)) {
2488 socket.on("setMotd", changeMOTD);
2489 changeMOTD();
2490}
2491
2492// setting MOTD logo
2493
2494if (UI_MOTDAutoLogo=="1") {
2495 if (MOTDAutoLogo_Mode!="1" && MOTDAutoLogo_Mode!="2" && MOTDAutoLogo_Mode!="3" && MOTDAutoLogo_Mode!="7") {
2496 MOTDAutoLogo_Mode='1';
2497 }
2498 if (MOTDAutoLogo_Mode=="3") {
2499 if (typeof MOTDAutoLogo_Interval!=="number" || MOTDAutoLogo_Interval<1) {
2500 MOTDAutoLogo_Interval=20;
2501 }
2502 setInterval(function() {
2503 nr=Math.floor(Math.random()*MOTDAutoLogo_Array.length);
2504 $("#motdlogo").attr('src', MOTDAutoLogo_Array[nr]);
2505 }, MOTDAutoLogo_Interval*1000);
2506 }
2507}
2508
2509// adding attention bar
2510
2511if (UI_AttentionBar=="1" && AttentionBar_URL!="") {
2512 attbarwrap = $('<div id="attbarrow-outer" class="col-md-12" />').prependTo("#announcements");
2513 attbar = $('<iframe id="attbar" width="100%" frameborder="0" scrolling="no" />')
2514 .attr('src', AttentionBar_URL)
2515 .appendTo(attbarwrap);
2516}
2517
2518// adding custom channel announcement
2519
2520if (UI_ChannelAnnouncement=="1") {
2521 ChannelAnnouncement_Title=="" ? ChannelAnnouncement_Title='Administration Message' : '';
2522 ChannelAnnouncement_HTML=="" ? ChannelAnnouncement_HTML='<i>no messages</i>' : '';
2523 makeAlert(ChannelAnnouncement_Title, ChannelAnnouncement_HTML).appendTo("#announcements");
2524}
2525
2526// adding full-width title bar and progress bar
2527
2528if (UI_FullTitleBar=="1") {
2529 titlerow = $('<div id="titlerow" class="row" />').insertBefore("#main");
2530 titlerowouter = $('<div id="titlerow-outer" class="col-md-12" />')
2531 .html($("#currenttitle").detach())
2532 .appendTo(titlerow);
2533 mediainfo = $('<p id="mediainfo" />').html('Nothing playing').prependTo("#videowrap");
2534
2535 UI_ProgressBar=="1" ? setInterval(function() {progressBar()}, 2000) : '';
2536
2537 socket.on("changeMedia", showInfo);
2538 showInfo();
2539
2540 if (UI_TitleIcon=="1" && TitleIcon_URL!="") {
2541 $("#titlerow #currenttitle").css({
2542 'background-image':'url("'+TitleIcon_URL+'")',
2543 'background-repeat':'no-repeat',
2544 'background-position':'left center'
2545 });
2546 }
2547}
2548
2549// changing title bar description
2550
2551if (UI_TitleBarDescription=="1") {
2552 socket.on("changeMedia", changeTitle);
2553 changeTitle();
2554}
2555
2556// easter egg
2557
2558function inba() {
2559 $("body").css('background-image', 'none');
2560 BGCHANGE++;
2561 BGCHANGE%2==0 ? $("body").css('background-color', 'gold') : $("body").css('background-color', 'blue');
2562}
2563
2564// customizing chat notifications sound
2565
2566if (UI_CustomPingSound=="1" && CustomPingSound_URL!="") {
2567 CHATSOUND = new Audio(CustomPingSound_URL);
2568 CHATSOUND.volume=0.6;
2569}
2570
2571// additional chat functions
2572
2573chatflair = $('<span id="chatflair" class="label label-success pull-right pointer">Func</span>')
2574 .insertAfter("#adminflair")
2575 .on("click", function() {
2576 if(!CHATFUNC) {
2577 $("#sounds-dropdown").remove();
2578 SOUNDSPANEL = false;
2579 showChatFunctions();
2580 CHATFUNC = true;
2581 } else {
2582 $("#chatfunc-dropdown").remove();
2583 CHATFUNC = false;
2584 }
2585 });
2586socket.on("rank", toggleChatFunctions);
2587toggleChatFunctions();
2588
2589// optional chat joining message
2590
2591if (UI_JoinText=="1") {
2592 JoinText_Message=="" ? JoinText_Message="joined" : '';
2593 socket.emit("chatMsg", {msg: '/me '+JoinText_Message});
2594}
2595
2596// optional chat leaving message
2597
2598if (UI_LeaveText=="1") {
2599 LeaveText_Message=="" ? LeaveText_Message="left" : '';
2600 $(window).unload(function() {
2601 socket.emit("chatMsg", {msg: '/me '+LeaveText_Message});
2602 });
2603}
2604
2605// adding chat buttons wrapping
2606
2607if (UI_FontsBtn=="1" || UI_EmotesBtn=="1" || UI_CommandsBtn=="1" || UI_SoundFilters=="1" || UI_ModPanel=="1" || UI_ChatSpeak=="1") {
2608 chatcontrols = $('<div id="chatcontrols" class="btn-group" />').appendTo("#chatwrap");
2609}
2610
2611// adding chat fonts button
2612
2613if (UI_FontsBtn=="1") {
2614 fontsbtn = $('<button id="fonts-btn" class="btn btn-sm btn-default" title="Display fonts panel" />')
2615 .html('<i class="glyphicon glyphicon-font"></i>')
2616 .appendTo(chatcontrols)
2617 .on("click", function() {
2618 toggleDiv(fontspanel);
2619 });
2620}
2621
2622// adding chat emotes button
2623
2624if (UI_EmotesBtn=="1") {
2625 emotesbtn = $('<button id="emotes-btn" class="btn btn-sm btn-default" title="Display emotes panel" />')
2626 .html('<i class="glyphicon glyphicon-picture"></i>')
2627 .appendTo(chatcontrols)
2628 .on("click", function() {
2629 toggleDiv(emotespanel);
2630 (UI_ChannelCache!="1" && !EMOTES) ? showEmotes() : '';
2631 });
2632}
2633
2634// adding chat commands button
2635
2636if (UI_CommandsBtn=="1" && (UI_UserCommands=="1" || UI_FontsBtn=="1" || UI_ChatSpeak=="1")) {
2637 chathelpbtn = $('<button id="chathelp-btn" class="btn btn-sm btn-default" />')
2638 .text('Chat Commands')
2639 .appendTo(chatcontrols)
2640 .on("click", function() {
2641 showChatHelp();
2642 });
2643}
2644
2645// adding chat sounds toggle button and control panel
2646
2647if (UI_SoundFilters=="1" || UI_ChatSpeak=="1") {
2648 voicesbtn = $('<button id="voices-btn" class="btn btn-sm btn-default" title="Mute chat voices" />')
2649 .html('<i class="glyphicon glyphicon-volume-down"></i>')
2650 .appendTo(chatcontrols)
2651 .on("click", function() {
2652 if(!SOUNDSPANEL) {
2653 $("#chatfunc-dropdown").remove();
2654 CHATFUNC=false;
2655 showSoundsPanel();
2656 SOUNDSPANEL = true;
2657 } else {
2658 $("#sounds-dropdown").remove();
2659 SOUNDSPANEL = false;
2660 }
2661 });
2662 VOICES=true;
2663}
2664
2665// adding moderators panel button
2666
2667if (UI_ModPanel=="1") {
2668 modbtn = $('<button id="mod-btn" class="btn btn-sm btn-default" title="Show moderators panel" />')
2669 .html('<i class="glyphicon glyphicon-tasks"></i>')
2670 .appendTo(chatcontrols)
2671 .on("click", function() {
2672 showModPanel();
2673 });
2674
2675 socket.on("rank", toggleModPanel);
2676 toggleModPanel();
2677}
2678
2679// adding player control buttons
2680
2681if (UI_PlayerOptions=="1") {
2682 if (UI_FullTitleBar=="1") {
2683 switchdescrbtn = $('<button id="switchdescr-btn" class="btn btn-sm btn-default" />')
2684 .attr('title', 'Switch description')
2685 .html('<span class="glyphicon glyphicon-info-sign"></span>')
2686 .appendTo("#playercontrols")
2687 .on("click", function() {
2688 DEFDESCR = !DEFDESCR;
2689 showInfo();
2690 });
2691 }
2692
2693 hideplayerbtn = $('<button id="hideplayer-btn" class="btn btn-sm btn-default" title="Hide player" />')
2694 .html('<span class="glyphicon glyphicon-ban-circle"></span>')
2695 .appendTo("#playercontrols")
2696 .on("click", function() {
2697 $(this).hasClass('btn-danger') ? showPlayer() : coverPlayer();
2698 });
2699
2700 muteplayerbtn = $('<button id="muteplayer-btn" class="btn btn-sm btn-default" title="Mute player" />')
2701 .append('<span class="glyphicon glyphicon-volume-off" />')
2702 .appendTo("#playercontrols")
2703 .on("click", function() {
2704 if ($(this).hasClass('btn-danger')) {
2705 $(this).removeClass('btn-danger').attr('title', 'Mute player');
2706 unmutePlayer();
2707 } else {
2708 $(this).addClass('btn-danger').attr('title', 'Unmute player');
2709 mutePlayer();
2710 }
2711 });
2712
2713 socket.on("changeMedia", toggleMuteBtn);
2714 toggleMuteBtn();
2715
2716 savemediabtn = $('<button id="savemedia-btn" class="btn btn-sm btn-default" title="Download" />')
2717 .html('<span class="glyphicon glyphicon-floppy-save"></span>')
2718 .appendTo("#playercontrols")
2719 .on("click", function() {
2720 downloadCurrentItem();
2721 });
2722}
2723
2724// adding player transformation buttons
2725
2726if (UI_TransformationBtns=="1") {
2727 transcontrols = $('<div id="transcontrols" class="btn-group pull-right" />').appendTo("#videowrap");
2728
2729 mirrorxbtn = $('<button id="mirrorx-btn" class="btn btn-sm btn-default" title="Mirror X player" />')
2730 .html('<span class="glyphicon glyphicon-resize-horizontal"></span>')
2731 .appendTo(transcontrols)
2732 .on("click", function() {
2733 if ($("#ytapiplayer").hasClass('mX')) {
2734 $("#ytapiplayer").removeClass('mX');
2735 } else {
2736 $("#ytapiplayer").addClass('mX');
2737 }
2738 });
2739
2740 mirrorybtn = $('<button id="mirrory-btn" class="btn btn-sm btn-default" title="Mirror Y player" />')
2741 .html('<span class="glyphicon glyphicon-resize-vertical"></span>')
2742 .appendTo(transcontrols)
2743 .on("click", function() {
2744 if ($("#ytapiplayer").hasClass('mY')) {
2745 $("#ytapiplayer").removeClass('mY');
2746 } else {
2747 $("#ytapiplayer").addClass('mY');
2748 }
2749 });
2750
2751 rotatebtn = $('<button id="rotate-btn" class="btn btn-sm btn-default" title="Rotate player" />')
2752 .html('<span class="glyphicon glyphicon-repeat"></span>')
2753 .appendTo(transcontrols)
2754 .on("click", function() {
2755 if ($("#ytapiplayer").hasClass('rotate')) {
2756 $("#ytapiplayer").removeClass('rotate');
2757 } else {
2758 $("#ytapiplayer").addClass('rotate');
2759 }
2760 });
2761
2762 verticalbtn = $('<button id="vertical-btn" class="btn btn-sm btn-default" title="Vertical player" />')
2763 .html('<span class="glyphicon glyphicon-arrow-up"></span>')
2764 .appendTo(transcontrols)
2765 .on("click", function() {
2766 if ($("#ytapiplayer").hasClass('vertical')) {
2767 $("#ytapiplayer").removeClass('vertical');
2768 this.attr('title', 'Vertical player');
2769 } else {
2770 $("#ytapiplayer").addClass('vertical');
2771 this.attr('title', 'Horizontal player');
2772 }
2773 });
2774}
2775
2776// creating fonts and emotes main row
2777
2778if (UI_FontsBtn=="1" || UI_EmotesBtn=="1") {
2779 chatpanel = $('<div id="chatpanel" class="row" />').insertBefore("#playlistrow");
2780}
2781
2782// adding fonts panel
2783
2784if (UI_FontsBtn=="1") {
2785 fontspanel = $('<div id="fontspanel" style="display:none" />').appendTo(chatpanel);
2786 fontsbtnwrap = $('<div id="fontsbtnwrap" />').appendTo(fontspanel);
2787
2788 FontsArray = [
2789 ['background:white', 'white', '■'],
2790 ['background:gold', 'yellow', '■'],
2791 ['background:orange', 'orange', '■'],
2792 ['background:#FFBBFF', 'pink', '■'],
2793 ['background:red', 'red', '■'],
2794 ['background:limegreen', 'lime', '■'],
2795 ['background:green', 'green', '■'],
2796 ['background:aqua', 'aqua', '■'],
2797 ['background:blue', 'blue', '■'],
2798 ['background:#660099', 'violet', '■'],
2799 ['background:#660000', 'brown', '■'],
2800 ['background:silver', 'silver', '■'],
2801 ['background:black', 'black', '■'],
2802 ['background:black; color:white', 'bw', 'a'],
2803 ['background:white; font-weight:bold; color:black', 'b', 'B'],
2804 ['background:white; font-style:italic; color:black', 'i', 'I'],
2805 ['background:white; text-decoration:underline; color:black', 'u', 'U'],
2806 ['background:white; text-decoration:line-through; color:black', 's', 's'],
2807 ['background:gold; color:red', 'd', 'D'],
2808 ['background:black; color:gold; font-family:impact, sans-serif', 'f', 'F'],
2809 ['background:black; color:white', 'sp', 'sp'],
2810 ['background:white; color:black; border:solid 2px red', '\\/', '\[\/\]'],
2811 ];
2812
2813 for (i in FontsArray) {
2814 $('<button id="cbtn'+i+'" onclick="insertText(\'['+FontsArray[i][1]+']\')" />')
2815 .addClass('btn btn-default').attr('style', FontsArray[i][0]).text(FontsArray[i][2])
2816 .appendTo(fontsbtnwrap);
2817 i%13==12 ? fontsbtnwrap.append('<br />') : false;
2818 }
2819
2820 if (UI_UnicodeChars=="1" && UnicodeChars_Array.length>0) {
2821 unibtnwrap = $('<div id="unibtnwrap" />').appendTo(fontspanel);
2822 for (i in UnicodeChars_Array) {
2823 btn=$('<button onclick="insertText(\''+UnicodeChars_Array[i]+'\')" />')
2824 .addClass('btn btn-default').text(UnicodeChars_Array[i])
2825 .appendTo(unibtnwrap);
2826 }
2827 }
2828}
2829
2830// adding emotes panel
2831
2832if (UI_EmotesBtn=="1") {
2833 emotespanel = $('<div id="emotespanel" style="display:none" />').appendTo(chatpanel);
2834 UI_ChannelCache=="1" ? showEmotes() : '';
2835}
2836
2837// adding background image to empty playlistrow corner
2838
2839if (UI_EmptyCornerBackground=="1" && EmptyCornerBackground.length>0) {
2840 rnd=Math.round(Math.random()*(EmptyCornerBackground.length-1));
2841 $("#playlistrow").css({
2842 'background-image':'url("'+EmptyCornerBackground[rnd]+'")', 'background-repeat':'no-repeat'
2843 });
2844}
2845
2846// adding layout configuration panel button
2847
2848layoutbtn = $('<button id="layout-btn" class="btn btn-sm btn-default btn-success pull-right" />')
2849 .html('<span class="glyphicon glyphicon-cog"></span> Layout')
2850 .prependTo("#leftpane")
2851 .on("click", function() {
2852 toggleConfigPanel();
2853 });
2854$("#playlistmanagerwrap").show();
2855
2856// adding media database and gallery wrap
2857
2858if (UI_ChannelDatabase=="1" || UI_ChannelGalleries=="1") {
2859 leftpanecontrols = $('<div id="leftpanecontrols" class="btn-group pull-left" />').insertAfter(layoutbtn);
2860}
2861
2862// adding media database button
2863
2864if (UI_ChannelDatabase=="1") {
2865 dbbtn = $('<button id="db-btn" class="btn btn-sm btn-default">Channel Database</button>')
2866 .appendTo(leftpanecontrols)
2867 .on("click", function() {
2868 toggleDiv(dbwrap);
2869 !CHANDB ? createDatabase() : '';
2870 });
2871}
2872
2873// adding galleries button
2874
2875if (UI_ChannelGalleries=="1") {
2876 gallerybtn = $('<button id="gallery-btn" class="btn btn-sm btn-default">Channel Galleries</button>')
2877 .appendTo(leftpanecontrols)
2878 .on("click", function() {
2879 toggleDiv(gallerywrap);
2880 !GALLERY ? createGallery() : '';
2881
2882 // patch: strange imgur behaviour (not loading first cached gallery)
2883 if(!GALLVIS && UI_ChannelCache=="1" && gallerywrap.css('display')!="none") {
2884 iframe=document.getElementById("galleryFrame");
2885 if(iframe.src.indexOf('imgur.com')>-1) {
2886 iframe.src=iframe.src;
2887 }
2888 }
2889 GALLVIS=true;
2890 });
2891}
2892
2893// adding layout configuration well
2894
2895configwrap = $('<div id="configwrap" class="col-lg-12 col-md-12" />').appendTo("#leftpane-inner");
2896configwell = $('<div id="config-well" class="well form-horizontal" />').appendTo(configwrap);
2897
2898if (!LAYOUTBOX) {
2899 toggleDiv(configwrap);
2900 layoutbtn.removeClass('btn-success');
2901}
2902
2903// adding layout configuration form
2904
2905configform = $('<div id="configform" class="form-group" />').appendTo(configwell);
2906$('<div class="col-lg-5 col-md-5">Global layout</div>').appendTo(configform);
2907configbtnwrap = $('<div id="configbtnwrap" class="col-lg-7 col-md-7" />').appendTo(configform);
2908
2909configbtn = $('<button id="config-btn" class="btn btn-default">Click to configure</button>')
2910 .appendTo(configbtnwrap)
2911 .on("click", function() {
2912 showConfig();
2913 });
2914
2915configbtnwrap.append('<br />');
2916
2917fluidlayout = $('<label class="checkbox-inline" />').appendTo(configbtnwrap);
2918cbox = $('<input type="checkbox" id="fluid-layout" value="no" />')
2919 .appendTo(fluidlayout)
2920 .on("click", function() {
2921 toggleFluidLayout();
2922 });
2923cbox.after(' Fluid');
2924
2925minlayout = $('<label class="checkbox-inline" />').appendTo(configbtnwrap);
2926cbox = $('<input type="checkbox" id="min-layout" value="no" />')
2927 .appendTo(minlayout)
2928 .on("click", function() {
2929 toggleMinLayout();
2930 });
2931cbox.after(' Minimized');
2932
2933// adding selector with player display modes
2934
2935if (UI_DisplayModeSel=="1") {
2936 modeform = $('<div id="modeform" class="form-group" />').appendTo(configwell);
2937 $('<div class="col-lg-5 col-md-5">Display mode</div>').appendTo(modeform);
2938 modewrap = $('<div id="modewrap" class="col-lg-7 col-md-7" />').appendTo(modeform);
2939
2940 modesel = $('<select id="mode-sel" class="form-control" />')
2941 .append('<option value="syMode">synchtube mode</option>')
2942 .append('<option value="kMode">cinema mode</option>')
2943 .append('<option value="chMode">chatroom mode</option>')
2944 .append('<option value="sMode">silent mode</option>')
2945 .append('<option value="rMode">radio mode</option>')
2946 .appendTo(modewrap)
2947 .on("change", function() {
2948 $("#config-btn, #configbtnwrap br").hide();
2949 $("#min-layout").parent().hide();
2950 $("#sounds-dropdown, #chatfunc-dropdown").remove();
2951 SOUNDSPANEL=false;
2952 CHATFUNC=false;
2953 PLAYER.type=="jw" ? refreshPlayer() : '';
2954 setMode($(this).val());
2955 scrollQueue();
2956 scrollChat();
2957 showPlayer();
2958 });
2959
2960 socket.on("changeMedia", setModeAfterVideoChange);
2961}
2962
2963// adding selector with channel themes
2964
2965themeform = $('<div id="themeform" class="form-group" />').appendTo(configwell);
2966$('<div class="col-lg-5 col-md-5">Personal theme</div>').appendTo(themeform);
2967themewrap = $('<div id="themewrap" class="col-lg-7 col-md-7" />').appendTo(themeform);
2968
2969themesel = $('<select id="theme-sel" class="form-control" />')
2970 .append('<option value="" class="theme-header" disabled>default themes</option>')
2971 .append('<option value="/css/themes/light.css"># Light</option>')
2972 .append('<option value="/css/themes/bootstrap-theme.min.css"># Bootstrap</option>')
2973 .append('<option value="/css/themes/slate.css"># Slate</option>')
2974 .append('<option value="/css/themes/cyborg.css"># Cyborg</option>')
2975 .appendTo(themewrap)
2976 .on("change", function() {
2977 $("#sounds-dropdown, #chatfunc-dropdown").remove();
2978 $("#playlistmanagerwrap").show();
2979 SOUNDSPANEL=false;
2980 CHATFUNC=false;
2981 USERTHEME=$(this).val();
2982 setUserCSS();
2983 setOpt(CHANNEL.name+"_theme", USERTHEME);
2984 });
2985
2986if (ThemesCSS.length>0) {
2987 themesel.append('<option value="" class="theme-header" disabled>additional themes</option>');
2988 for (i in ThemesCSS) {
2989 themesel.append('<option value="'+ThemesCSS[i][1]+'">'+ThemesCSS[i][0]+'</option>');
2990 }
2991}
2992
2993if (UI_ChannelTheme=="1" && ChannelThemeURL!="") {
2994 themesel.prepend('<option value="'+ChannelThemeURL+'"># Channel Theme</option>')
2995 .prepend('<option value="" class="theme-header" disabled>main theme</option>');
2996}
2997
2998themesel.val(USERTHEME);
2999
3000// adding temporary hiding options
3001
3002hideform = $('<div id="hideform" class="form-group" />').appendTo(configwell);
3003$('<div class="col-lg-5 col-md-5 conf-cap">Temporary hide</div>').appendTo(hideform);
3004hidewrap = $('<div id="hidewrap" class="col-lg-7 col-md-7" />').appendTo(hideform);
3005
3006hidemotd = $('<label class="checkbox-inline" />').appendTo(hidewrap);
3007cbox = $('<input type="checkbox" id="hide-motd" value="no" >')
3008 .appendTo(hidemotd)
3009 .on("click", function() {
3010 toggleDiv("#motdrow");
3011 });
3012cbox.after(' MOTD');
3013
3014hideann = $('<label class="checkbox-inline" />').appendTo(hidewrap);
3015cbox = $('<input type="checkbox" id="hide-ann" value="no" />')
3016 .appendTo(hideann)
3017 .on("click", function() {
3018 toggleDiv("#announcements");
3019 });
3020cbox.after(' Announcements');
3021
3022hidetitle = $('<label class="checkbox-inline" />').appendTo(hidewrap);
3023cbox = $('<input type="checkbox" id="hide-title" value="no" />')
3024 .appendTo(hidetitle)
3025 .on("click", function() {
3026 toggleDiv("#titlerow");
3027 toggleDiv("#currenttitle");
3028 });
3029cbox.after(' Title');
3030
3031hidepl = $('<label class="checkbox-inline" />').appendTo(hidewrap);
3032cbox = $('<input type="checkbox" id="hide-pl" value="no" />')
3033 .appendTo(hidepl)
3034 .on("click", function() {
3035 toggleDiv("#queue");
3036 toggleDiv("#plmeta");
3037 });
3038cbox.after(' Playlist');
3039
3040hidehf = $('<label class="checkbox-inline" />').appendTo(hidewrap);
3041cbox = $('<input type="checkbox" id="hide-hf" value="no" />')
3042 .appendTo(hidehf)
3043 .on("click", function() {
3044 $("nav").css('display')!="none" ? headerMode("fixed") : headerMode(USERCONFIG.header);
3045 toggleDiv("nav");
3046 toggleDiv("footer");
3047 });
3048cbox.after(' H&F');
3049
3050// adding embedding options
3051
3052if (UI_EmbeddingMedia=="1" && (EmbeddingMedia_Images!="" || EmbeddingMedia_Videos!="")) {
3053 embedform = $('<div id="embedform" class="form-group" />').appendTo(configwell);
3054 $('<div class="col-lg-5 col-md-5 conf-cap">Embeds <span id="embed-help">[?]</span></div>')
3055 .appendTo(embedform);
3056 embedwrap = $('<div id="embedwrap" class="col-lg-7 col-md-7" />').appendTo(embedform);
3057
3058 $("#embed-help").on("click", function() {
3059 txt = 'This option lets you see images or videos directly on the chat, instead of links.\n'
3060 + 'Click on image or double click on video to open in the new tab.\n'
3061 + 'All videos are muted by default, if autoplay - click to unmute, else click to play.\n\n'
3062 + 'This channel supports following types of links (specified as CSS codes):\n'
3063 + '■ Images - ';
3064 (EmbeddingMedia_Images!="") ? txt+=EmbeddingMedia_Images : 'none';
3065 txt += '\n■ Videos - ';
3066 (EmbeddingMedia_Videos!="") ? txt+=EmbeddingMedia_Videos : 'none';
3067 alert(txt);
3068 });
3069
3070 if (EmbeddingMedia_Images!="") {
3071 embedimg = $('<label class="checkbox-inline" />').appendTo(embedwrap);
3072 cbox = $('<input type="checkbox" id="embed-img" checked>')
3073 .appendTo(embedimg)
3074 .on("click", function() {
3075 EMBEDIMG = !EMBEDIMG;
3076 setOpt(CHANNEL.name+"_embedimg", EMBEDIMG);
3077 });
3078 cbox.after(' img');
3079 !EMBEDIMG ? cbox.removeAttr('checked') : '';
3080 }
3081
3082 if (EmbeddingMedia_Videos!="") {
3083 embedvid = $('<label class="checkbox-inline" />').appendTo(embedwrap);
3084 cbox = $('<input type="checkbox" id="embed-webm" checked>')
3085 .appendTo(embedvid)
3086 .on("click", function() {
3087 EMBEDVID = !EMBEDVID;
3088 setOpt(CHANNEL.name+"_embedvid", EMBEDVID);
3089 EMBEDVID ? autovid.show() : autovid.hide();
3090 });
3091 cbox.after(' video');
3092 !EMBEDVID ? cbox.removeAttr('checked') : '';
3093
3094 autovid = $('<label class="checkbox-inline" />').appendTo(embedwrap);
3095 cbox = $('<input type="checkbox" id="auto-webm" checked>')
3096 .appendTo(autovid)
3097 .on("click", function() {
3098 AUTOVID = !AUTOVID;
3099 setOpt(CHANNEL.name+"_autovid", AUTOVID);
3100 });
3101 cbox.after(' autoplay');
3102 !AUTOVID ? cbox.removeAttr('checked') : '';
3103 !EMBEDVID ? autovid.hide() : '';
3104 }
3105}
3106
3107// adding quick commands and volume buttons
3108
3109if (UI_QuickCommandsBtns=="1" || UI_VolumeBtns=="1") {
3110 funcbtnform = $('<div id="funcbtnform" class="form-group" />').appendTo(configwell);
3111 $('<div class="col-lg-5 col-md-5">Command buttons</div>').appendTo(funcbtnform);
3112 funcbtnwrap = $('<div id="funcbtnwrap" class="col-lg-7 col-md-7" />').appendTo(funcbtnform);
3113 btnsgroup = $('<div id="funcbtngroup" class="btn-group" />').appendTo(funcbtnwrap);
3114
3115 if (UI_QuickCommandsBtns=="1") {
3116 clearbtn = $('<button id="clear-btn" class="btn btn-default btn-sm">/clear</button>')
3117 .appendTo(btnsgroup)
3118 .on("click", function() {
3119 if (confirm('Are you sure to clear the chat window?')) {
3120 socket.emit("chatMsg", {msg: '/clear'});
3121 }
3122 });
3123 afkbtn = $('<button id="afk-btn" class="btn btn-default btn-sm">/afk</button>')
3124 .appendTo(btnsgroup)
3125 .on("click", function() {
3126 socket.emit("chatMsg", {msg: '/afk'});
3127 });
3128
3129 socket.on("rank", toggleClearBtn);
3130 toggleClearBtn();
3131 }
3132
3133 if (UI_VolumeBtns=="1") {
3134 voldownbtn = $('<button id="voldown-btn" class="btn btn-default btn-sm">vol -</button>')
3135 .appendTo(btnsgroup)
3136 .on("click", function() {
3137 a=PLAYER.player.getVolume();
3138 PLAYER.player.setVolume(a-1);
3139 });
3140 volupbtn = $('<button id="volup-btn" class="btn btn-default btn-sm">vol +</button>')
3141 .appendTo(btnsgroup)
3142 .on("click", function() {
3143 a=PLAYER.player.getVolume();
3144 PLAYER.player.setVolume(a+1);
3145 });
3146
3147 socket.on("changeMedia", toggleVolBtn);
3148 toggleVolBtn();
3149 }
3150}
3151
3152// adding media database layout
3153
3154if (UI_ChannelDatabase=="1" && ChannelDatabase_URL=="") {
3155 dbwrap = $('<div id="dbwrap" class="col-lg-12 col-md-12" style="display:none" />').insertBefore(configwrap);
3156 dbwell = $('<div id="db-well" class="well" />').appendTo(dbwrap);
3157
3158 var item_nr=0;
3159 var layer_nr=1;
3160 var opening=new Array();
3161 var item_count=new Array(0);
3162 var count_nr=0;
3163
3164 if (ChannelDatabase.length<1 || ChannelDatabase[0][0]!="") {
3165 ChannelDatabase.unshift(['', '(various media)']);
3166 }
3167 UI_ChannelCache=="1" ? createDatabase() : '';
3168} else if (UI_ChannelDatabase=="1" && ChannelDatabase_URL!="") {
3169 $.getScript(ChannelDatabase_URL);
3170}
3171
3172// adding galleries layout
3173
3174if (UI_ChannelGalleries=="1") {
3175 gallerywrap = $('<div id="gallerywrap" class="col-lg-12 col-md-12" style="display:none" />')
3176 .appendTo("#leftpane-inner");
3177 gallerywell = $('<div id="gallery-well" class="well" />').appendTo(gallerywrap);
3178
3179 if (ChannelGalleries_Array.length<1) {
3180 ChannelGalleries_Array=[['Anime pictures', 'http://imgur.com/a/SjwJb/embed']];
3181 }
3182 UI_ChannelCache=="1" ? createGallery() : '';
3183}
3184
3185// unchecking temporary media checkbox for registered users
3186
3187if (UI_DefaultNonTemp=="1") {
3188 CLIENT.rank>0 ? $(".add-temp").prop('checked', false) : '';
3189}
3190
3191// adding playlist options for moderators button
3192
3193advplaylist = $('<button id="advplaylist" class="btn btn-sm btn-default" title="Advanced options" />')
3194 .append('<span class="glyphicon glyphicon-wrench" />')
3195 .insertBefore("#qlockbtn")
3196 .on("click", function() {
3197 toggleDiv(advplcontrols);
3198 });
3199
3200// adding advanced playlist options form
3201
3202advplcontrol = $('<div id="advplcontrol" class="col-lg-12 col-md-12" />').insertAfter("#playlistmanager");
3203advplcontrols = $('<div id="advplcontrols" class="btn-group" style="display:none" />').appendTo(advplcontrol);
3204
3205// adding advanced playlist options buttons
3206
3207playnextbtn = $('<button id="playnext-btn" class="btn btn-default">Play next</button>')
3208 .appendTo(advplcontrols)
3209 .on("click", function() {
3210 socket.emit("playNext");
3211 });
3212
3213addrandombtn = $('<button id="addrandom-btn" class="btn btn-default">Add random</button>')
3214 .appendTo(advplcontrols)
3215 .on("click", function() {
3216 addRandomItem();
3217 });
3218
3219bumplastbtn = $('<button id="bumplast-btn" class="btn btn-default">Bump last</button>')
3220 .appendTo(advplcontrols)
3221 .on("click", function() {
3222 len=$("#queue").children().length;
3223 uid=$("#queue .queue_entry:nth-child("+len+")").data("uid");
3224 socket.emit("moveMedia", {from:uid, after:PL_CURRENT});
3225 });
3226
3227deletelastbtn = $('<button id="deletelast-btn" class="btn btn-default">Delete last</button>')
3228 .appendTo(advplcontrols)
3229 .on("click", function() {
3230 if (confirm('Are you sure to delete last item?')) {
3231 len=$("#queue").children().length;
3232 uid=$("#queue .queue_entry:nth-child("+len+")").data("uid");
3233 socket.emit("delete", uid);
3234 }
3235 });
3236
3237toggleAdvancedPl();
3238
3239// adding playlist expanding button
3240
3241expandbtn = $('<button id="expand-btn" class="btn btn-sm btn-default" title="Expand playlist" />')
3242 .append('<span class="glyphicon glyphicon-resize-full" />')
3243 .prependTo("#videocontrols")
3244 .on("click", function() {
3245 expandQueue();
3246 });
3247
3248// adding playlist scrolling button
3249
3250scrollbtn = $('<button id="scroll-btn" class="btn btn-sm btn-default" title="Scroll playlist to current item" />')
3251 .append('<span class="glyphicon glyphicon-hand-right" />')
3252 .prependTo("#videocontrols")
3253 .on("click", function() {
3254 scrollQueue();
3255 });
3256
3257// adding contributors button
3258
3259contribbtn = $('<button id="contrib-btn" class="btn btn-sm btn-default" title="Contributors list" />')
3260 .append('<span class="glyphicon glyphicon-user" />')
3261 .prependTo("#videocontrols")
3262 .on("click", function() {
3263 showContributors();
3264 });
3265
3266// adding pin-up button
3267
3268pinupbtn = $('<button id="pinup-btn" class="btn btn-sm btn-default" title="Pin playlist to player" />')
3269 .append('<span class="glyphicon glyphicon-pushpin" />')
3270 .prependTo("#videocontrols")
3271 .on("click", function() {
3272 !PINNED ? pinUp() : unPin();
3273 scrollQueue();
3274 scrollChat();
3275 });
3276
3277// extending 'Get URLs' function
3278
3279if (UI_ExtendedGetURLs=="1") {
3280 $("#getplaylist").unbind()
3281 .on("click", function() {
3282 getPlaylistURLs();
3283 });
3284}
3285
3286// altering message for the first-timers
3287
3288if ($("#plonotification").length>0) {
3289 repl = '"the old style" of playlist buttons (<b>recommended</b>) - more compact playlist with nice icons '
3290 + '(see image <a href="https://dl.dropboxusercontent.com/s/4ya7i5vlyb3likk/oldpl.jpg" target="_blank">'
3291 + 'here</a>).';
3292 html=$("#plonotification .alert").html().replace(/the old style of playlist buttons./, repl);
3293 html=html.replace('right click). ', 'right click).<br />');
3294 $("#plonotification .alert").html(html);
3295}
3296
3297// rearranging footer
3298
3299html='<br />CyTube Plus 4.5 · Copyright © 2013-2014 Zimny Lech · '
3300 + 'Free source on <a href="http://github.com/zimny-lech/CyTube-Plus" target="_blank">GitHub</a> · '
3301 + '<a href="http://github.com/zimny-lech/CyTube-Plus/wiki" target="_blank">Wiki</a>';
3302$(".credit").append(html);
3303
3304if (UI_CustomRightFooter=="1") {
3305 rightfooter = $('<span id="rightfooter">'+CustomRightFooter_HTML+'</span>')
3306 .appendTo("footer .container");
3307}
3308
3309if (UI_CustomFooter=="1" || UI_UserStatistics=="1") {
3310 leftfooter = (UI_CustomRightFooter=="1") ? $('<span id="leftfooter" />') : $('<div id="leftfooter" />');
3311 (UI_CustomRightFooter=="1" && CustomFooter_HTML!="") ? leftfooter.html(CustomFooter_HTML) : '';
3312 leftfooter.appendTo("footer .container");
3313}
3314
3315// updating user visits
3316
3317USERVISITS++;
3318setOpt(CHANNEL.name+"_visits", USERVISITS);
3319
3320if (UI_UserStatistics=="1") {
3321 (UI_CustomFooter=="1" && CustomFooter_HTML!="") ? $('<br /><br />').appendTo(leftfooter) : '';
3322
3323 $('<span>My visits: </span><span class="badge footer-badge">'+USERVISITS+'</span><span> / </span>')
3324 .appendTo(leftfooter);
3325 $('<span>Current online time: </span>').appendTo(leftfooter);
3326 onlinetime = $('<span id="onlinetime" class="badge footer-badge">0:00</span>').appendTo(leftfooter);
3327
3328 setInterval(function() {onlineTime()}, 60000);
3329}
3330
3331/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3332
3333/* ----- Chat and window extensions and events ----- */
3334
3335/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3336
3337(UI_EmbeddingMedia=="1" && (EmbeddingMedia_Images!="" || EmbeddingMedia_Videos!="")) ? ALTERCHATFORMAT=true : '';
3338(UI_UserMarks=="1" || UI_IndependentEmotes=="1" || UI_IndependentFilters=="1") ? ALTERCHATFORMAT=true : '';
3339
3340// alter chat messages formatting
3341// DEV NOTE: this is extended function from CyTube "util.js" file
3342
3343if (ALTERCHATFORMAT) {
3344 function formatChatMessage(data, last) {
3345 if (!data.meta || data.msgclass) {
3346 data.meta = {addClass:data.msgclass, addClassToNameAndTimestamp:data.msgclass};
3347 }
3348
3349 skip=data.username===last.name;
3350 data.meta.addClass==="server-whisper" ? skip=true : '';
3351 data.msg.match(/^\s*<strong>\w+\s*:\s*<\/strong>\s*/) ? skip=false : '';
3352 data.meta.forceShowName ? skip=false : '';
3353 data.msg=execEmotes(data.msg);
3354
3355 last.name = data.username;
3356 div = $('<div />');
3357 if (data.meta.addClass==="drink") {
3358 div.addClass('drink');
3359 data.meta.addClass='';
3360 }
3361
3362 if (USEROPTS.show_timestamps) {
3363 time = $('<span class="timestamp" />').appendTo(div);
3364 timestamp = new Date(data.time).toTimeString().split(' ')[0];
3365 time.text('['+timestamp+'] ');
3366 if (data.meta.addClass && data.meta.addClassToNameAndTimestamp) {
3367 time.addClass(data.meta.addClass);
3368 }
3369 }
3370
3371 if (UI_UserMarks=="1" && UI_Squavatars=="1") {
3372 html=createSquavatar(data.username);
3373 div.html(div.html()+html);
3374 }
3375 if (UI_UserMarks=="1" && UI_Squavatars!="1") {
3376 if (UserMarks_Array[data.username]!=undefined) {
3377 html='<span class="avatar">'+UserMarks_Array[data.username]+'</span>';
3378 div.html(div.html()+html);
3379 }
3380 }
3381
3382 uname = $('<span />');
3383 !skip ? uname.appendTo(div) : '';
3384 mark = (UI_UsernameMark=="1" && UsernameMark_Char!="") ? UsernameMark_Char : ':';
3385 $('<strong class="username" />').text(data.username+mark+' ').appendTo(uname);
3386
3387 data.meta.modflair ? uname.addClass(getNameColor(data.meta.modflair)) : '';
3388 if (data.meta.addClass && data.meta.addClassToNameAndTimestamp) {
3389 uname.addClass(data.meta.addClass);
3390 }
3391 if (data.meta.superadminflair) {
3392 uname.addClass('globalmod label').addClass(data.meta.superadminflair.labelclass);
3393 $('<span class="glyphicon" />').addClass(data.meta.superadminflair.icon)
3394 .prependTo(uname);
3395 }
3396
3397 message = $('<span />').appendTo(div);
3398 message[0].innerHTML=data.msg;
3399
3400 (data.meta.addClass=="greentext") ? message.addClass('greentext') : '';
3401 (data.meta.addClass=="spoiler") ? message.addClass('spoiler') : '';
3402 (data.meta.addClass=="action") ? message.addClass('action') : '';
3403 (data.meta.addClass=="server-whisper") ? message.addClass('server-whisper') : '';
3404
3405 if (data.meta.action) {
3406 uname.remove();
3407 message[0].innerHTML=data.username+' '+data.msg;
3408 }
3409
3410 if (UI_IndependentEmotes=="1") {
3411 _div=div.html();
3412 for (i in IndependentEmotes) {
3413 filter=IndependentEmotes[i][0];
3414 html = '<img src="'+IndependentEmotes[i][1]+'" title="'+filter+'" '
3415 + 'style="width:'+IndependentEmotes[i][2]+'px; '
3416 + 'height:'+IndependentEmotes[i][3]+'px; '
3417 + 'cursor:pointer" onclick="insertText(\''+filter+'\')" />';
3418 re=new RegExp(filter, 'g');
3419 _div=_div.replace(re, html);
3420 }
3421 div.html(_div);
3422 }
3423 if (UI_IndependentFilters=="1") {
3424 _div=div.html();
3425 for (i in IndependentFilters) {
3426 _div=_div.replace(IndependentFilters[i].before, IndependentFilters[i].after);
3427 }
3428 div.html(_div);
3429 }
3430
3431 data.meta.addClass ? message.addClass(data.meta.addClass) : '';
3432 data.meta.shadow ? div.addClass("chat-shadow") : '';
3433 div.find("img").load(function() {
3434 SCROLLCHAT ? scrollChat() : '';
3435 });
3436
3437 if (EMBEDIMG && UI_EmbeddingMedia=="1") {
3438 div.find(EmbeddingMedia_Images).each(function() {
3439 img = $('<img class="embedimg" />').attr('src', this.href)
3440 .load(function() {
3441 SCROLLCHAT ? scrollChat() : '';
3442 });
3443 $(this).html(img);
3444 });
3445 }
3446 if (EMBEDVID && UI_EmbeddingMedia=="1") {
3447 div.find(EmbeddingMedia_Videos).each(function() {
3448 vid = $('<video class="embedvid" />').attr('src', this.href).prop('loop', 'true')
3449 .load(function() {
3450 SCROLLCHAT ? scrollChat() : '';
3451 }).on("click", function() {
3452 if (!AUTOVID) {
3453 if ($(this).get(0).paused) {
3454 $(this).get(0).play();
3455 } else {
3456 $(this).get(0).pause();
3457 }
3458 } else {
3459 $(this).prop('muted', !$(this).prop('muted'));
3460 };
3461 return false;
3462 }).on("dblclick", function() {
3463 window.open(this.src, '_blank');
3464 return false;
3465 });
3466 AUTOVID ? vid.prop('autoplay', 'true').prop('muted', 'true') : '';
3467 UI_MediaControls=="1" ? vid.attr('controls', '') : '';
3468 $(this).html(vid);
3469 });
3470 }
3471
3472 return div;
3473 }
3474}
3475
3476// client-side chat buffer for playing sounds
3477
3478_chatBuffer=addChatMessage;
3479addChatMessage=function(data) {
3480 if (UI_SoundFilters=="1" && VOICES && (!(data.username in MUTEDVOICES) || MUTEDVOICES[data.username]=="0")) {
3481 for (i in SoundFilters_Array) {
3482 if (data.msg.indexOf(i)>-1) {
3483 aud=new Audio(SoundFilters_Array[i]);
3484 aud.volume=SOUNDSVALUES[SOUNDSLVL];
3485 aud.play();
3486 }
3487 }
3488 }
3489 if (UI_ChatSpeak=="1" && VOICES && (!(data.username in MUTEDVOICES) || MUTEDVOICES[data.username]=="0")) {
3490 msg=getText(data.msg)
3491 if (msg.indexOf("!mow ")>=0) {
3492 str=msg.split("!mow ");
3493 aud=new Audio(SPEAKLINK+'?lang=polish&text='+encodeURI(str[1]));
3494 aud.volume=SOUNDSVALUES[SOUNDSLVL];
3495 aud.play();
3496 } else if (msg.indexOf("!say ")>=0) {
3497 str=msg.split("!say ");
3498 aud=new Audio(SPEAKLINK+'?lang=english&text='+encodeURI(str[1]));
3499 aud.volume=SOUNDSVALUES[SOUNDSLVL];
3500 aud.play();
3501 }
3502 }
3503 _chatBuffer(data);
3504}
3505
3506// fix formatting and sending chat messages
3507// DEV NOTE: this are extended events from CyTube "util.js" file
3508
3509$("#chatline, #chatbtn").unbind();
3510
3511$("#chatline").on("keydown", function(ev) {
3512 if (ev.keyCode==13) {
3513 if (CHATTHROTTLE) {
3514 return;
3515 }
3516 _msg=$("#chatline").val();
3517 msg=$("#chatline").val();
3518 if (msg.trim()) {
3519 msg=prepareMessage(msg.trim());
3520 meta={};
3521 if (COMMAND) {
3522 socket.emit("chatMsg", {msg:_msg});
3523 msg='➥ '+msg;
3524 COMMAND=false;
3525 }
3526 if (USEROPTS.adminhat && CLIENT.rank>=255) {
3527 msg='/a '+msg;
3528 } else if (USEROPTS.modhat && CLIENT.rank>=Rank.Moderator) {
3529 meta.modflair=CLIENT.rank;
3530 }
3531 if (CLIENT.rank>=2 && msg.indexOf("/m ")===0) {
3532 meta.modflair=CLIENT.rank;
3533 msg=msg.substring(3);
3534 }
3535 socket.emit("chatMsg", {msg:msg, meta:meta});
3536 userChatStats(_msg);
3537 CHATHIST.push($("#chatline").val());
3538 CHATHISTIDX=CHATHIST.length;
3539 $("#chatline").val('');
3540 }
3541 return;
3542 } else if (ev.keyCode==9) {
3543 chatTabComplete();
3544 ev.preventDefault();
3545 return false;
3546 } else if (ev.keyCode==38) {
3547 if (CHATHISTIDX==CHATHIST.length) {
3548 CHATHIST.push($("#chatline").val());
3549 }
3550 if(CHATHISTIDX>0) {
3551 CHATHISTIDX--;
3552 $("#chatline").val(CHATHIST[CHATHISTIDX]);
3553 }
3554 ev.preventDefault();
3555 return false;
3556 } else if (ev.keyCode==40) {
3557 if (CHATHISTIDX<CHATHIST.length-1) {
3558 CHATHISTIDX++;
3559 $("#chatline").val(CHATHIST[CHATHISTIDX]);
3560 }
3561 ev.preventDefault();
3562 return false;
3563 }
3564});
3565
3566$("#chatbtn").on("click", function() {
3567 _msg=$("#chatline").val();
3568 msg=$("#chatline").val();
3569 if (msg.trim()) {
3570 msg=prepareMessage(msg.trim());
3571 if (COMMAND) {
3572 socket.emit("chatMsg", {msg:_msg});
3573 msg='➥ '+msg;
3574 COMMAND=false;
3575 }
3576 socket.emit("chatMsg", {msg:msg});
3577 userChatStats(_msg);
3578 $("#chatline").val('');
3579 }
3580});
3581
3582// fix layout behaviour after resizing
3583// DEV NOTE: this is extended function from CyTube "util.js" file
3584
3585function resizeStuff() {
3586 VWIDTH = $("#videowrap").width() + "";
3587 VHEIGHT = Math.floor(parseInt(VWIDTH) * 9 / 16 + 1) + "";
3588 $("#ytapiplayer").width(VWIDTH).height(VHEIGHT);
3589
3590 if (!$("body").hasClass("fluid")) {
3591 return;
3592 }
3593
3594 var h = parseInt(VHEIGHT) - $("#chatline").outerHeight() - 1;
3595 $("#messagebuffer").height(h);
3596 $("#userlist").height(h);
3597
3598 if (UI_DisplayModeSel=="1") {
3599 m=modesel.val();
3600
3601 // patches for various display modes
3602 if (m=="chMode" || m=="rMode") {
3603 if (WEBKIT) {
3604 $("#videowrap").hide();
3605 } else {
3606 $("#videowrap div, #videowrap p").hide();
3607 $("#ytapiplayer").width(1).height(1);
3608 }
3609 fitChat("auto");
3610 } else if (m=="syMode" && USERCONFIG.player=="center") {
3611 fitChat(200);
3612 } else if (m=="sMode") {
3613 // DEV NOTE: current function is called in "changeMedia" callback (condition race)
3614
3615 VW=$("#messagebuffer").width();
3616 VH=Math.floor(parseInt(VW)*9/16+1);
3617 $("#messagebuffer, #userlist").height(VH);
3618 }
3619 }
3620}
3621
3622// bind new resizing function
3623
3624$(window).unbind("resize");
3625$(window).resize(resizeStuff);
3626
3627/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3628
3629// setting global sockets
3630
3631socket.on("channelOpts", setUserCSS);
3632socket.on("channelCSSJS", setUserCSS);
3633
3634socket.on("login", patchWrap);
3635socket.on("rank", toggleAdvancedPl);
3636
3637// setting final layout after loading
3638
3639setLayout();
3640scrollChat();
3641scrollQueue();
3642
3643if (FLUID) {
3644 $(".container").removeClass('container').addClass('container-fluid');
3645 $("footer .container-fluid").removeClass('container-fluid').addClass('container');
3646 $("#fluid-layout").prop('checked', 'true');
3647 $("#fontspanel, #emotespanel").addClass('fluidpanel');
3648}
3649
3650// finishing variable
3651
3652LOADED=true;
3653
3654// Google Analytics code
3655
3656(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
3657(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
3658m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
3659})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
3660ga('create', 'UA-53755606-1', 'auto');
3661ga('send', 'pageview');
3662
3663// adding external script file
3664
3665if (UI_ExternalScript=="1" && ExternalScript_URL!="") {
3666 $.getScript(ExternalScript_URL);
3667}
3668
3669
3670// ==UserScript==
3671// @name Google Drive Video Player for CyTube
3672// @namespace gdcytube
3673// @description Play Google Drive videos on CyTube
3674// @include http://lolcow.tv/r/*
3675// @include https://lolcow.tv/r/*
3676// @include https://cytu.be/r/*
3677// @include https://www.cytu.be/r/*
3678// @grant unsafeWindow
3679// @grant GM_xmlhttpRequest
3680// @grant GM.xmlHttpRequest
3681// @connect docs.google.com
3682// @run-at document-end
3683// @version 1.7.0
3684// ==/UserScript==
3685
3686try {
3687 function debug(message) {
3688 try {
3689 unsafeWindow.console.log('[Drive]', message);
3690 } catch (error) {
3691 unsafeWindow.console.error(error);
3692 }
3693 }
3694
3695 function httpRequest(opts) {
3696 if (typeof GM_xmlhttpRequest === 'undefined') {
3697 // Assume GM4.0
3698 debug('Using GM4.0 GM.xmlHttpRequest');
3699 GM.xmlHttpRequest(opts);
3700 } else {
3701 debug('Using old-style GM_xmlhttpRequest');
3702 GM_xmlhttpRequest(opts);
3703 }
3704 }
3705
3706 var ITAG_QMAP = {
3707 37: 1080,
3708 46: 1080,
3709 22: 720,
3710 45: 720,
3711 59: 480,
3712 44: 480,
3713 35: 480,
3714 18: 360,
3715 43: 360,
3716 34: 360
3717 };
3718
3719 var ITAG_CMAP = {
3720 43: 'video/webm',
3721 44: 'video/webm',
3722 45: 'video/webm',
3723 46: 'video/webm',
3724 18: 'video/mp4',
3725 22: 'video/mp4',
3726 37: 'video/mp4',
3727 59: 'video/mp4',
3728 35: 'video/flv',
3729 34: 'video/flv'
3730 };
3731
3732 function getVideoInfo(id, cb) {
3733 var url = 'https://docs.google.com/get_video_info?authuser='
3734 + '&docid=' + id
3735 + '&sle=true'
3736 + '&hl=en';
3737 debug('Fetching ' + url);
3738
3739 httpRequest({
3740 method: 'GET',
3741 url: url,
3742 onload: function (res) {
3743 try {
3744 debug('Got response ' + res.responseText);
3745
3746 if (res.status !== 200) {
3747 debug('Response status not 200: ' + res.status);
3748 return cb(
3749 'Google Drive request failed: HTTP ' + res.status
3750 );
3751 }
3752
3753 var data = {};
3754 var error;
3755 // Google Santa sometimes eats login cookies and gets mad if there aren't any.
3756 if(/accounts\.google\.com\/ServiceLogin/.test(res.responseText)){
3757 error = 'Google Docs request failed: ' +
3758 'This video requires you be logged into a Google account. ' +
3759 'Open your Gmail in another tab and then refresh video.';
3760 return cb(error);
3761 }
3762
3763 res.responseText.split('&').forEach(function (kv) {
3764 var pair = kv.split('=');
3765 data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
3766 });
3767
3768 if (data.status === 'fail') {
3769 error = 'Google Drive request failed: ' +
3770 unescape(data.reason).replace(/\+/g, ' ');
3771 return cb(error);
3772 }
3773
3774 if (!data.fmt_stream_map) {
3775 error = (
3776 'Google has removed the video streams associated' +
3777 ' with this item. It can no longer be played.'
3778 );
3779
3780 return cb(error);
3781 }
3782
3783 data.links = {};
3784 data.fmt_stream_map.split(',').forEach(function (item) {
3785 var pair = item.split('|');
3786 data.links[pair[0]] = pair[1];
3787 });
3788 data.videoMap = mapLinks(data.links);
3789
3790 cb(null, data);
3791 } catch (error) {
3792 unsafeWindow.console.error(error);
3793 }
3794 },
3795
3796 onerror: function () {
3797 var error = 'Google Drive request failed: ' +
3798 'metadata lookup HTTP request failed';
3799 error.reason = 'HTTP_ONERROR';
3800 return cb(error);
3801 }
3802 });
3803 }
3804
3805 function mapLinks(links) {
3806 var videos = {
3807 1080: [],
3808 720: [],
3809 480: [],
3810 360: []
3811 };
3812
3813 Object.keys(links).forEach(function (itag) {
3814 itag = parseInt(itag, 10);
3815 if (!ITAG_QMAP.hasOwnProperty(itag)) {
3816 return;
3817 }
3818
3819 videos[ITAG_QMAP[itag]].push({
3820 itag: itag,
3821 contentType: ITAG_CMAP[itag],
3822 link: links[itag]
3823 });
3824 });
3825
3826 return videos;
3827 }
3828
3829 /*
3830 * Greasemonkey 2.0 has this wonderful sandbox that attempts
3831 * to prevent script developers from shooting themselves in
3832 * the foot by removing the trigger from the gun, i.e. it's
3833 * impossible to cross the boundary between the browser JS VM
3834 * and the privileged sandbox that can run GM_xmlhttpRequest().
3835 *
3836 * So in this case, we have to resort to polling a special
3837 * variable to see if getGoogleDriveMetadata needs to be called
3838 * and deliver the result into another special variable that is
3839 * being polled on the browser side.
3840 */
3841
3842 /*
3843 * Browser side function -- sets gdUserscript.pollID to the
3844 * ID of the Drive video to be queried and polls
3845 * gdUserscript.pollResult for the result.
3846 */
3847 function getGoogleDriveMetadata_GM(id, callback) {
3848 debug('Setting GD poll ID to ' + id);
3849 unsafeWindow.gdUserscript.pollID = id;
3850 var tries = 0;
3851 var i = setInterval(function () {
3852 if (unsafeWindow.gdUserscript.pollResult) {
3853 debug('Got result');
3854 clearInterval(i);
3855 var result = unsafeWindow.gdUserscript.pollResult;
3856 unsafeWindow.gdUserscript.pollResult = null;
3857 callback(result.error, result.result);
3858 } else if (++tries > 100) {
3859 // Took longer than 10 seconds, give up
3860 clearInterval(i);
3861 }
3862 }, 100);
3863 }
3864
3865 /*
3866 * Sandbox side function -- polls gdUserscript.pollID for
3867 * the ID of a Drive video to be queried, looks up the
3868 * metadata, and stores it in gdUserscript.pollResult
3869 */
3870 function setupGDPoll() {
3871 unsafeWindow.gdUserscript = cloneInto({}, unsafeWindow);
3872 var pollInterval = setInterval(function () {
3873 if (unsafeWindow.gdUserscript.pollID) {
3874 var id = unsafeWindow.gdUserscript.pollID;
3875 unsafeWindow.gdUserscript.pollID = null;
3876 debug('Polled and got ' + id);
3877 getVideoInfo(id, function (error, data) {
3878 unsafeWindow.gdUserscript.pollResult = cloneInto({
3879 error: error,
3880 result: data
3881 }, unsafeWindow);
3882 });
3883 }
3884 }, 1000);
3885 }
3886
3887 var TM_COMPATIBLES = [
3888 'Tampermonkey',
3889 'Violentmonkey' // https://github.com/calzoneman/sync/issues/713
3890 ];
3891
3892 function isTampermonkeyCompatible() {
3893 try {
3894 return TM_COMPATIBLES.indexOf(GM_info.scriptHandler) >= 0;
3895 } catch (error) {
3896 return false;
3897 }
3898 }
3899
3900 if (isTampermonkeyCompatible()) {
3901 unsafeWindow.getGoogleDriveMetadata = getVideoInfo;
3902 } else {
3903 debug('Using non-TM polling workaround');
3904 unsafeWindow.getGoogleDriveMetadata = exportFunction(
3905 getGoogleDriveMetadata_GM, unsafeWindow);
3906 setupGDPoll();
3907 }
3908
3909 unsafeWindow.console.log('Initialized userscript Google Drive player');
3910 unsafeWindow.hasDriveUserscript = true;
3911 // Checked against GS_VERSION from data.js
3912 unsafeWindow.driveUserscriptVersion = '1.7';
3913} catch (error) {
3914 unsafeWindow.console.error(error);
3915}
3916
3917
3918/* ----- END OF LIBRARY ----- */