//default channels list var channels = ["freecodecamp", "streamerhouse", "terenasiii", "gtimetv", "twitchplayspokemon", "ogaminglol", "greentekken", "esl_csgo", "towelliee", "ogamingsc2", "syndicate", "lirik", "cretetion", "comster404", "brunofin" ]; var channelsProcessed = null; var toProcess = null; var addNewCards = false; var isCardExpanded = true; var version = { 'name': 'Accept', 'value': 'application/vnd.twitchtv.3+json' }; var clientID = 'pjykme02lwchi1998nx2m2phpmcxw0'; var endpoint = 'https://api.twitch.tv/kraken'; var cards = {}; function anyCaseStringInArray(array, item) { for (var i in array) { if (array[i].toLowerCase() === item.toLowerCase()) { return true; } } return false; } function uniqueItems(array) { var dictionary = {}; var uniqueItemsArray = []; for (var i in array) { var item = array[i]; if (!dictionary[item]) { dictionary[item] = true; uniqueItemsArray.push(item); } } return uniqueItemsArray; } function getStreamInfo(channelName) { var card = cards[channelName]; var url = '/streams/' + card.id; var finalURL = endpoint + url; if (card.meta.unavailable) { setStreamInfo(null, card); } else { sendRequest(finalURL); } } function setStreamInfo(channelStream, card) { if (channelStream === null) { //error with channel if (card.meta.unavailable) { card.content.description = [card.meta.errorStatus]; } } else { var channelName = channelStream._links.channel.split('/')[5]; card = cards[channelName]; if (channelStream.stream === null) { //channel offline card.meta.onlineStatus = "offline"; card.content.description = ["Offline, check after sometime."]; } else { //channel online card.meta.onlineStatus = "online"; var game = channelStream.stream.game; var viewers = channelStream.stream.viewers; card.content.description = [game, viewers + " watching"]; var video_height = channelStream.stream.video_height; var video_definition = ""; if (video_height < 720) { video_definition = "SD"; } else if (video_height < 1080) { video_definition = "HD"; } else if (video_height < 2160) { video_definition = "FULL HD"; } else { video_definition = "4K UHD"; } card.content.notes.push(video_definition); //rename buttons as "Watch Now" card.action.name = "Watch Now"; } } //time the display of cards such that //live channels have a higher probability of showing up first if (card.meta.unavailable) { window.setTimeout(function() { displayCard(card); }, 1000); } else if (card.onlineStatus === 'offline') { window.setTimeout(function() { displayCard(card); }, 500); } else { displayCard(card); } } function afterLoad(cardID) { //show addChannel-btn $('#channelEdit-div').removeClass('hidden'); //enable card view button $('#btn-card-view').removeClass('disabled'); //enable btn-reload-streams $('#btn-reload-streams').removeClass('disabled'); //disable getChannels timer window.clearTimeout(getChannelTimeout); //addNewCards if (addNewCards) { var scrollToID = '#' + cardID; TweenLite.to(window, 1, { scrollTo: scrollToID, ease: Power1.easeInOut }); addNewCards = false; } } var timeoutID = null; function displayCard(card) { $('#streams-div').append(getCard(card)); channelsProcessed += 1; //after completion tasks if (channelsProcessed === channelstoProcess) { window.clearTimeout(timeoutID); afterLoad(card.id); } else { //if for some reason all cards cannot be processed to the end, //re-enable the controls anyway. window.clearTimeout(timeoutID); timeoutID = window.setTimeout(function() { afterLoad(card.id); }, 2000); } } /*the card object var Card = { 'id' : id, 'image' : {'bg': {'type' : type, 'value' : value}, 'title': channelName}, 'content' : {'notes' : [], 'description' : []}, 'action' : {'name' : linkName, 'link' : linkValue}, 'meta' : {'unavailable' : false, 'errorNo' : null, 'errorStatus' : "", 'onlineStatus' : 'offline'} };*/ function getCard(card) { //initialize card var cardNode = document.createElement('div'); $(cardNode).addClass('card item channel-info-card').attr('id', card.id); //image var image = document.createElement('div'); $(image).addClass('card-image'); //background if (card.image.bg.type === 'image') { var img = document.createElement('img'); $(img).attr('src', card.image.bg.value); $(image).append(img); } else if (card.image.bg.type === 'error') { var error_div = document.createElement('div'); $(error_div).addClass('channel-error'); var error_symbol = document.createElement('i'); $(error_symbol).attr('aria-hidden', 'true'); if (card.image.bg.value === 'ban') { $(error_symbol).addClass('fa fa-ban'); } else if (card.image.bg.value === 'notFound') { $(error_symbol).addClass('fa fa-times'); } $(error_div).append($(error_symbol)); $(image).append(error_div); } //title var cardTitle = document.createElement('span'); $(cardTitle).addClass('card-title'); var title = document.createElement('span'); $(title).addClass('channel-title').text(card.image.title); $(cardTitle).append(title); $(image).append(cardTitle); //content var content = document.createElement('div'); $(content).addClass('card-content'); //notes var notes = document.createElement('div'); $(notes).addClass('channel-notes'); $.each(card.content.notes, function(key, value) { var note = document.createElement('span'); $(note).addClass('note').text(value); $(notes).append(note); }); $(content).append(notes); //description var description = document.createElement('p'); $(description).addClass('main-text'); var description_list = document.createElement('ul'); $.each(card.content.description, function(key, value) { var listItem = document.createElement('li'); $(listItem).text(value); $(description_list).append(listItem); }); $(description).append(description_list); $(content).append(description); //buttons var action = document.createElement('div'); $(action).addClass('card-action'); var button = document.createElement('a'); $(button).text(card.action.name).attr(card.action.link); if (card.meta.unavailable) { $(button).addClass('disabled'); $(action).addClass('card-status-unavailable'); } else if (card.meta.onlineStatus === 'online') { $(action).addClass('card-status-online'); } else if (card.meta.onlineStatus === 'offline') { $(action).addClass('card-status-offline'); } $(action).append(button); //status if (card.meta.unavailable) { $(cardNode).addClass("offline"); } else { $(cardNode).addClass(card.meta.onlineStatus); } //set card view if (isCardExpanded) { $(cardNode).addClass('medium'); } else { $(content).addClass('hidden'); } //append all nodes to card $(cardNode).append(image); $(cardNode).append(content); $(cardNode).append(action); //return card HTMLElement return $(cardNode)[0]; } function setChannelInfo(channel) { /*var name = channel.name; var logo = channel.logo; var status = channel.status; var broadcaster_language = channel.broadcaster_language; var channelURL = channel.url; var mature = channel.mature;*/ //make card var card = {}; card.image = {}; card.content = {}; card.meta = {}; //set image //background if (channel.hasOwnProperty('error')) { if (channel.status === 404) { card.image.bg = { 'type': 'error', 'value': 'notFound' }; card.meta.errorNo = 404; card.meta.errorStatus = 'User not found.'; } else if (channel.status === 422) { card.image.bg = { 'type': 'error', 'value': 'ban' }; card.meta.errorNo = 422; card.meta.errorStatus = 'User is deleted or banned.'; } card.meta.unavailable = true; } else { card.image.bg = { 'type': 'image', 'value': channel.logo }; } if (card.meta.unavailable) { //set id var errMsg = channel.message; var channelName = errMsg.match(/'(.*)'/).pop(); card.id = channelName; //title card.image.title = channelName; //buttons var action = { 'name': 'Not Available', 'link': { 'href': '#', 'target': '_blank' } }; card.action = action; } else { //set id card.id = channel.name; //title card.image.title = channel.name; card.meta.unavailable = false; //content //notes var notes = [channel.broadcaster_language]; //set note as mature if true if (channel.mature) { notes.push("mature"); } card.content.notes = notes; //buttons var action = { 'name': 'Visit Channel', 'link': { 'href': channel.url, 'target': '_blank' } }; card.action = action; } cards[card.id] = card; getStreamInfo(card.id); } function handleResponse(data) { if (data.hasOwnProperty('stream')) { setStreamInfo(data); } else { setChannelInfo(data); } } function sendRequest(url, value, call) { var reqURL = url + '?' + 'client_id=' + clientID + '&' + 'api_version=3' + '&' + 'callback=handleResponse'; //attacth script var script = document.createElement('script'); script.setAttribute('src', reqURL); document.head.appendChild(script); //remove the script document.head.removeChild(script); } //show or hide "to top" floating button window.addEventListener('scroll', displayControlPanel); function displayControlPanel() { var controlPanel = document.getElementById('control-panel'); var controlPanelPos = controlPanel.getBoundingClientRect(); if (controlPanelPos.bottom <= 0) { $('#toTop').css('visibility', 'visible'); } else { $('#toTop').css('visibility', 'hidden'); } } //buttons //reload stream info document.getElementById('btn-reload-streams').addEventListener("click", function() { var streams_div = document.getElementById('streams-div'); while (streams_div.hasChildNodes()) { streams_div.removeChild(streams_div.lastChild); } //set cards filter to all document.getElementById('cards_all').click(); //get channel info and display them getChannels(channels); }); //card view document.getElementById('btn-card-view').addEventListener('click', function() { //alternative views of cards $.each($('.channel-info-card'), function(key, element) { $(element).find('.card-content').toggleClass('hidden'); $(element).toggleClass('medium'); }); //toggle button name if ($('#btn-card-view').find('.fa').hasClass('fa-compress')) { //change compress icon to expand $('#btn-card-view').find('.fa').removeClass('fa-compress'); $('#btn-card-view').find('.fa').addClass('fa-expand'); //change button name to Expand $('#btn-card-view').find('.control-panel-btn-txt').text = "Expand"; } else { //change compress icon to compress $('#btn-card-view').find('.fa').removeClass('fa-expand'); $('#btn-card-view').find('.fa').addClass('fa-compress'); //change button name to expand $('#btn-card-view').find('.control-panel-btn-txt').text = "Compact"; } if (isCardExpanded) { isCardExpanded = false; } else { isCardExpanded = true; } }); //cards filter $('.card-filter').on('click', function() { //change button appearance targetElementId = this.id; $.each($('.card-filter'), function(key, element) { //console.log($(element).attr('id'), $(this).attr('id'), $(this).attr('id')===$(element).attr('id')); if ($(element).attr('id') === targetElementId) { $(element).removeClass('lighten-2 z-depth-0'); } else { $(element).addClass('lighten-2 z-depth-0'); } }); switch (targetElementId) { case "cards_all": $('.hidden-card').removeClass('hidden-card'); break; case "cards_online": $('.online').removeClass('hidden-card'); $('.offline').addClass('hidden-card'); break; case "cards_offline": $('.offline').removeClass('hidden-card'); $('.online').addClass('hidden-card'); break; default: console.log("cannot filter cards: ", targetElementId); break; } }); //scroll to top document.getElementById('toTop').addEventListener("click", function() { TweenLite.to(window, 1, { scrollTo: 0, ease: Power1.easeInOut }); }); //addChannel modal var addChannelInput = document.getElementById('addChannel-input'); addChannelInput.addEventListener('keypress', function(event) { var key = event.which || event.keyCode; if (key === 13) { //reset duplicate message $('#addChannel-msg').addClass('hidden'); var channel = (addChannelInput.value).trim(); //check if the channel is already diplayed on the page if (anyCaseStringInArray(channels, channel)) { $('#addChannel-msg').empty(); $('#addChannel-msg').append("<strong>" + channel + "</strong>" + " channel is already displayed on the page. No need to add it."); $('#addChannel-msg').removeClass('hidden'); } else { if ((channel.includes(" "))) { $('#addChannel-msg').empty(); $('#addChannel-msg').append("Channel names cannot contain spaces."); $('#addChannel-msg').removeClass('hidden'); } else if (/\S/.test(channel)) { //make sure there are no non-whitespace characters in the channel name like:

var newChip = "<div class='chip'>" + channel + "<i class='fa fa-times close' aria-hidden='true'></i></div>"; $('#addChannel-chips').append(newChip); } } addChannelInput.value = ""; } }); document.getElementById('addChannel-btn').addEventListener('click', function() { $('#addChannel-modal').openModal(); document.getElementById('addChannel-input').focus(); }); document.getElementById('addChannel-add').addEventListener('click', function() { var chips = document.getElementById('addChannel-chips').childNodes; var newChannels = []; //add all the channels in the HTMLCollection for (var i = 0; i < chips.length; i++) { var value = chips.item(i).textContent.trim().toLowerCase(); if (value !== "" && value !== null && value !== undefined && !(value.includes(" "))) { newChannels.push(value); } } //make sure new channels are not repeated //otherwise a card can be shown more than once var newUniqueChannels = uniqueItems(newChannels); //also add to the default channel list //this makes sure user added channels survive a refresh channels = channels.concat(newUniqueChannels); //clear the chips var chipsNode = document.getElementById('addChannel-chips'); while (chipsNode.hasChildNodes()) { chipsNode.removeChild(chipsNode.lastChild); } if (newUniqueChannels.length > 0) { addNewCards = true; getChannels(newUniqueChannels); } //set cards filter to all document.getElementById('cards_all').click(); }); //remove channels modal document.getElementById('removeChannel-btn').addEventListener('click', function() { $('#removeChannel-modal').openModal(); $('#removeChannel-list').empty(); $.each(cards, function(key, card) { var cardName = card.id; var optionNodeHTML = "<p class='removeChannel-opts'><input type='checkbox' class='filled-in' id='opt-" + cardName + "' checked='checked' /><label for='opt-" + cardName + "'>" + card.id + "</label></p>"; $('#removeChannel-list').append(optionNodeHTML); }); var options = document.getElementsByClassName('removeChannel-opts'); //make the <p> region clickable for (var i = 0; i < options.length; i++) { options.item(i).addEventListener('click', function() { this.getElementsByTagName('input')[0].click(); }); } }); //remove channels document.getElementById('removeChannels-rem').addEventListener('click', function() { $.each($('#removeChannel-list').children(), function(key, value) { var option = $(value).find('input'); if (!$(option).is(':checked')) { var cardID = $(option).attr('id').replace('opt-', ''); //remove the card from the page $('#' + cardID).remove(); //remove the channel from the channels array var cardIndex = channels.indexOf(cardID); if (cardIndex >= 0) { channels.splice(cardIndex, 1); } //remove card from the cards object delete cards[cardID]; } }); }); getChannels(channels); var getChannelTimeout = null; //get channel info and display as cards function getChannels(channels) { channelsProcessed = 0; channelstoProcess = channels.length; if (channelstoProcess <= 0) { return null; } for (var i in channels) { var url = '/channels/' + channels[i]; var channelName = channels[i]; var finalURL = endpoint + url; sendRequest(finalURL); } if (!addNewCards) { cards = {}; } //disable card view button document.getElementById('btn-card-view').classList.add('disabled'); //disable btn-reload-streams document.getElementById('btn-reload-streams').classList.add('disabled'); //hide addChannel-btn document.getElementById('channelEdit-div').classList.add('hidden'); //if for some reason cards fail to load, re-enable controls //after 6 seconds getChannelTimeout = window.setTimeout(function() { //enable card view button document.getElementById('btn-card-view').classList.remove('disabled'); //enable btn-reload-streams document.getElementById('btn-reload-streams').classList.remove('disabled'); //show addChannel-btn document.getElementById('channelEdit-div').classList.remove('hidden'); }, 6000); }

!