PubNub Demos http://kboh.codes/pubnub
<!-- | |
/** | |
* @author Kevin Bohinski <bohinsk1@tcnj.edu> | |
* @version 1.0 | |
* @since 2016-3-13 | |
* | |
* Project Name: PubNub SoundCloud Demo | |
* Description: • Demonstrate understanding of PubNub | |
* | |
* Filename: index.html | |
* Description: HTML File, with embedded JS for PubNub. | |
* Last Modified: 2016-3-17 | |
*/ | |
--> | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<!-- HTML meta key, value, pairs --> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<!-- HTML meta author, description, and title --> | |
<meta name="author" content="https://keybase.io/kbohinski"> | |
<meta name="description" content="PubNub SoundCloud!"> | |
<title>PubNub SoundCloud!</title> | |
<!-- CDN stylesheets to clean up display --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"/> | |
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootswatch/3.3.4/paper/bootstrap.min.css"/> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"/> | |
<!-- Bootstrap's required javascript --> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> | |
<!-- IE9 bootstrap support --> | |
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> | |
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |
<!--[if lt IE 9]> | |
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | |
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | |
<![endif]--> | |
<!-- JS --> | |
<!-- CDN in the PubNub & SoundCloud JS SDK --> | |
<script src="http://cdn.pubnub.com/pubnub-3.14.3.min.js"></script> | |
<script src="https://w.soundcloud.com/player/api.js"></script> | |
<script src="https://cdn.rawgit.com/broofa/node-uuid/master/uuid.js"></script> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>PubNub SoundCloud!</h1> | |
<ul style="padding-top: 65px; padding-bottom: 65px;"> | |
<li><a href="/pubnub/tictactoe.html">PubNub Tic-Tac-Toe</a></li> | |
<li><a href="https://gist.github.com/kbohinski/cb3fee469351f3a40cf5">Source</a></li> | |
</ul> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<iframe id="song" width="100%" height="600" scrolssling="no" frameborder="no" | |
src="https://w.soundcloud.com/player/?url=https://soundcloud.com/ideaot/macklerena4&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe> | |
</div> | |
</div> | |
<div id="messages"></div> | |
</div> | |
</body> | |
<script type="text/javascript"> | |
/* Uses ECMAScript's strict mode */ | |
"use strict"; | |
/* Wrap in an IIFE to avoid pollution of the global namespace. */ | |
let iife = (function () { | |
/* Set user id and last message to avoid collisions */ | |
let userId = uuid.v4(); | |
let last = {time: 0}; | |
/* Set PubNub Keys */ | |
let PUBNUB_PUB_KEY = 'pub-c-a0d12b56-2353-4a7c-b021-d0bab6fcfbdf'; | |
let PUBNUB_SUB_KEY = 'sub-c-9682eaf8-ec9d-11e5-8112-02ee2ddab7fe'; | |
/* Setup SoundCloud object */ | |
let soundcloud = SC.Widget('song'); | |
let SONG_URL = 'https://soundcloud.com/ideaot/macklerena'; | |
let ms_delay = 250; | |
/* Setup Date object for millisecond calculations */ | |
let d = new Date(); | |
/* Load in song */ | |
soundcloud.load(SONG_URL, { | |
'auto_play': false, | |
'visual': true | |
}); | |
/** | |
* Helper function to add a message to the DOM. | |
* | |
* @param time : Position in song from start (milliseconds) | |
* @param from : Who the message was from | |
* @param type : Type of the message | |
*/ | |
function addMessage(time, from, type) { | |
/* Check if the message to add is from ourselves */ | |
if (from === userId) { | |
from = 'You'; | |
} else { | |
from = 'Someone Else: ' + from; | |
} | |
/* Grab div to put messages into */ | |
let messages = document.getElementById('messages'); | |
/* Make new node to add to the DOM */ | |
let newNode = document.createElement('DIV'); | |
newNode.innerHTML = '<div class="panel panel-default"><div class="panel-body"><p><b>' + type + '</b></p><p>' + time + '</p><span class="text-muted">' + from + '</span></div></div>'; | |
/* Insert new node */ | |
messages.insertBefore(newNode, messages.childNodes[0]); | |
} | |
/** | |
* Helper function to check if the milliseconds changed significantly to prevent an infinite loop. | |
* | |
* @param lastTime : The time from the last message | |
* @param newTime : The time from the incoming message | |
* @returns {boolean} : If the difference is significant. | |
*/ | |
function checkRange(lastTime, newTime) { | |
if (lastTime === undefined) { | |
return true; | |
} else { | |
return (Math.abs(lastTime - newTime) > 10); | |
} | |
} | |
/** | |
* Helper function to check if the song is playing | |
* | |
* @returns {boolean} : If the song is playing | |
*/ | |
function isPlaying() { | |
soundcloud.isPaused(function (status) { | |
return !status; | |
}); | |
} | |
/* Instantiate a new instance of PubNub */ | |
let pub_nub = PUBNUB.init({ | |
publish_key: PUBNUB_PUB_KEY, | |
subscribe_key: PUBNUB_SUB_KEY, | |
uuid: userId, | |
error: function (error) { | |
console.log('Error:', error); | |
} | |
}); | |
/* Subscribe to the channel */ | |
pub_nub.subscribe({ | |
channel: 'audiosync_' + SONG_URL, | |
message: function (m) { | |
/* Handle incoming messages */ | |
if (m.type === 'seek') { | |
if ((m.from !== userId) && checkRange(last.time, m.time)) { | |
ms_delay = d.getTime - m.timeSent; | |
soundcloud.seekTo(parseFloat(m.time.toFixed(1) + ms_delay)); | |
} | |
addMessage(m.time.toFixed(1), m.from, m.type); | |
} else if (m.type === 'play' && !isPlaying() && last.type !== 'play') { | |
soundcloud.play(); | |
addMessage('Status', m.from, m.type); | |
} else if (m.type === 'pause' && isPlaying() && last.type !== 'pause') { | |
soundcloud.pause(); | |
addMessage('Status', m.from, m.type); | |
} | |
last = m; | |
}, | |
error: function (error) { | |
console.log('Error: ' + JSON.stringify(error)); | |
} | |
}); | |
/* Whenever the user seeks within the song, change the song position on the other browser */ | |
soundcloud.bind(SC.Widget.Events.SEEK, function (time) { | |
/* Check to see if position in song changed */ | |
if (last.time.toFixed(1) !== time.currentPosition.toFixed(1)) { | |
pub_nub.publish({ | |
channel: 'audiosync_' + SONG_URL, | |
message: { | |
type: 'seek', | |
time: time.currentPosition, | |
from: userId, | |
timeSent: d.getTime() | |
} | |
}); | |
} | |
}); | |
/* Whenever the user plays, play the song on the other browser */ | |
soundcloud.bind(SC.Widget.Events.PLAY, function () { | |
if (last.type !== 'play') { | |
pub_nub.publish({ | |
channel: 'audiosync_' + SONG_URL, | |
message: { | |
type: 'play', | |
time: 0, | |
from: userId | |
} | |
}); | |
} | |
}); | |
/* Whenever the user pauses, pause the song on the other browser */ | |
soundcloud.bind(SC.Widget.Events.PAUSE, function () { | |
if (last.type !== 'pause') { | |
pub_nub.publish({ | |
channel: 'audiosync_' + SONG_URL, | |
message: { | |
type: 'pause', | |
time: 0, | |
from: userId | |
} | |
}); | |
} | |
}); | |
/* Return public interface so it can be used outside of the IIFE */ | |
return { | |
pn: pub_nub, | |
sc: soundcloud | |
}; | |
})(); | |
</script> | |
</html> |
<!-- | |
/** | |
* @author Kevin Bohinski <bohinsk1@tcnj.edu> | |
* @version 1.0 | |
* @since 2016-3-13 | |
* | |
* Project Name: PubNub Tic-Tac-Toe Demo | |
* Description: • Demonstrate understanding of PubNub | |
* | |
* Filename: tictactoe.html | |
* Description: HTML File, with embedded JS for PubNub. | |
* Last Modified: 2016-3-17 | |
*/ | |
--> | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<!-- HTML meta key, value, pairs --> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<!-- HTML meta author, description, and title --> | |
<meta name="author" content="https://keybase.io/kbohinski"> | |
<meta name="description" content="PubNub Tic-Tac-Toe!"> | |
<title>PubNub Tic-Tac-Toe!</title> | |
<!-- CDN stylesheets to clean up display --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"/> | |
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootswatch/3.3.4/paper/bootstrap.min.css"/> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"/> | |
<!-- Bootstrap's required javascript --> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> | |
<!-- IE9 bootstrap support --> | |
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> | |
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |
<!--[if lt IE 9]> | |
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | |
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | |
<![endif]--> | |
<!-- CSS --> | |
<style> | |
.btn-fix { | |
width: 100%; | |
height: 100%; | |
display: table-cell; | |
font-size: 20px; | |
} | |
#form { | |
padding-bottom: 65px; | |
} | |
#messages { | |
padding-top: 65px; | |
} | |
.alert h2 { | |
color: white; | |
} | |
</style> | |
<!-- JS --> | |
<!-- CDN in the PubNub JS SDK --> | |
<script src="http://cdn.pubnub.com/pubnub-3.14.3.min.js"></script> | |
<script src="https://cdn.rawgit.com/broofa/node-uuid/master/uuid.js"></script> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>PubNub Tic-Tac-Toe!</h1> | |
<ul style="padding-top: 65px; padding-bottom: 65px;"> | |
<li><a href="/pubnub/index.html">PubNub SoundCloud</a></li> | |
<li><a href="https://gist.github.com/kbohinski/cb3fee469351f3a40cf5">Source</a></li> | |
</ul> | |
<div id="room-info" class="alert alert-info"><p><strong>Room Number:</strong></p> | |
<div id="room"></div> | |
<p>Use this room number to allow others to enter your room.</p> | |
</div> | |
<div id="xoAlert" class="alert alert-danger"><p><strong>Red/Blue:</strong></p> | |
<div id="xo"></div> | |
</div> | |
<form id="form"> | |
<div class="form-group"> | |
<label for="roomid">If you wish to enter another room, enter the room number here. Otherwise, just press | |
start!</label> | |
<input id="roomid" name="roomid" class="form-control" type="text"> | |
</div> | |
<div class="form-group"> | |
<input name="submit" value="Start!" class="btn btn-primary" type="submit" id="submit"> | |
</div> | |
</form> | |
<div id="board"> | |
<div class="row"> | |
<div class="col-xs-4"> | |
<a href="#" id="00" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="10" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="20" class="btn btn-default btn-fix">-</a> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-xs-4"> | |
<a href="#" id="01" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="11" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="21" class="btn btn-default btn-fix">-</a> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-xs-4"> | |
<a href="#" id="02" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="12" class="btn btn-default btn-fix">-</a> | |
</div> | |
<div class="col-xs-4"> | |
<a href="#" id="22" class="btn btn-default btn-fix">-</a> | |
</div> | |
</div> | |
</div> | |
<div id="messages"></div> | |
</div> | |
</body> | |
<script type="text/javascript"> | |
/* Uses ECMAScript's strict mode */ | |
"use strict"; | |
/* Wrap in an IIFE to avoid pollution of the global namespace. */ | |
let iife = (function () { | |
/* Fix width on page load */ | |
window.onload = function () { | |
resizeBoxes(); | |
}; | |
/** | |
* Helper function to fix box size | |
*/ | |
function resizeBoxes() { | |
for (let x = 0; x < 3; x++) { | |
for (let y = 0; y < 3; y++) { | |
$('#' + x.toString() + y.toString()).height($('#' + x.toString() + y.toString()).parent().width()); | |
$('#' + x.toString() + y.toString()).width($('#' + x.toString() + y.toString()).parent().width()); | |
} | |
} | |
} | |
/* Set user info and last message to avoid collisions */ | |
let userId = uuid.v4(); | |
let xo = 'red'; | |
let ROOM_ID = Math.floor(Math.random() * 9000) + 1000; | |
let last = {from: 'blue'}; | |
/* Display room id and if they are x or o */ | |
let roomDisplay = document.getElementById('room'); | |
let xoDisplay = document.getElementById('xo'); | |
roomDisplay.innerHTML = ROOM_ID; | |
xoDisplay.innerHTML = 'You are playing as : ' + xo; | |
/* Set PubNub Keys */ | |
let PUBNUB_PUB_KEY = 'pub-c-a0d12b56-2353-4a7c-b021-d0bab6fcfbdf'; | |
let PUBNUB_SUB_KEY = 'sub-c-9682eaf8-ec9d-11e5-8112-02ee2ddab7fe'; | |
/* Instantiate a new instance of PubNub */ | |
let pub_nub = PUBNUB.init({ | |
publish_key: PUBNUB_PUB_KEY, | |
subscribe_key: PUBNUB_SUB_KEY, | |
uuid: userId, | |
error: function (error) { | |
console.log('Error:', error); | |
} | |
}); | |
/* Subscribe to current room, to see if other user has joined and is starting the game */ | |
pub_nub.subscribe({ | |
channel: 'tictactoe_' + ROOM_ID, | |
message: function (m) { | |
handleMessage(m); | |
}, | |
error: function (error) { | |
console.log('Error: ' + JSON.stringify(error)); | |
} | |
}); | |
/* If the user switches the room, switch it, and start the game on both ends */ | |
let submit = document.getElementById('submit'); | |
submit.addEventListener('click', function (e) { | |
/* Prevent link from being navigated to */ | |
e.preventDefault(); | |
/* If it is a different room, change some displays */ | |
if (document.getElementById('roomid').value !== ROOM_ID && document.getElementById('roomid').value !== '') { | |
ROOM_ID = document.getElementById('roomid').value; | |
roomDisplay.innerHTML = ROOM_ID; | |
xo = 'blue'; | |
xoDisplay.innerHTML = 'You are playing as : ' + xo; | |
document.getElementById('xoAlert').style.backgroundColor = '#2196F3'; | |
} | |
/* Subscribe, and send a message to start the game */ | |
pub_nub.subscribe({ | |
channel: 'tictactoe_' + ROOM_ID, | |
message: function (m) { | |
handleMessage(m); | |
}, | |
error: function (error) { | |
console.log('Error: ' + JSON.stringify(error)); | |
} | |
}); | |
pub_nub.publish({ | |
channel: 'tictactoe_' + ROOM_ID, | |
message: { | |
type: 'start', | |
from: xo | |
} | |
}); | |
handleMessage({type: 'start', from: xo}); | |
}, false); | |
/** | |
* Helper function to add a message to the DOM. | |
* | |
* @param value : Value of the message | |
* @param from : Who the message was from | |
* @param type : Type of the message | |
*/ | |
function addMessage(value, from, type) { | |
/* Check if the message to add is from ourselves */ | |
if (from === xo) { | |
from = 'You'; | |
} else { | |
from = 'Someone Else: ' + from; | |
} | |
/* Grab div to put messages into */ | |
let messages = document.getElementById('messages'); | |
/* Make new node to add to the DOM */ | |
let newNode = document.createElement('DIV'); | |
newNode.innerHTML = '<div class="panel panel-default"><div class="panel-body"><p><b>' + type + '</b></p><p>' + value + '</p><span class="text-muted">' + from + '</span></div></div>'; | |
/* Insert new node */ | |
messages.insertBefore(newNode, messages.childNodes[0]); | |
} | |
/** | |
* Helper function to handle incoming PubNub messages. | |
* Checks to see if game is being started, or a move was made. | |
* On new move, checks for a win condition. | |
* | |
* @param m : The message | |
*/ | |
function handleMessage(m) { | |
/* Add message to DOM for display */ | |
addMessage(m.node, m.from, m.type); | |
if (m.type === 'start') { | |
/* Start the game, change some displays */ | |
document.getElementById('form').innerHTML = ''; | |
document.getElementById('room-info').innerHTML = '<b>The game has started.</b><br>Red goes first.'; | |
/* Set this so red goes first */ | |
last = {from: 'blue'}; | |
} else { | |
/* Dont listen to cheaters :) */ | |
if (m.from !== last.from) { | |
/* Get the button they pressed, and change its color and value */ | |
let el = document.getElementById(m.node); | |
let red = 'btn btn-danger disabled btn-fix'; | |
let blue = 'btn btn-primary disabled btn-fix'; | |
if (m.from === 'red') { | |
el.className = red; | |
el.innerHTML = 'X'; | |
} else { | |
el.className = blue; | |
el.innerHTML = 'O'; | |
} | |
/* Check for vertical or horizontal win conditions */ | |
let checkVer = false; | |
let checkHor = false; | |
for (let x = 0; x < 3; x++) { | |
let el0 = document.getElementById('' + x.toString() + '0').className; | |
let el1 = document.getElementById('' + x.toString() + '1').className; | |
let el2 = document.getElementById('' + x.toString() + '2').className; | |
if (el0 === red && el1 === red && el2 === red) { | |
checkVer = true; | |
break; | |
} else if (el0 === blue && el1 === blue && el2 === blue) { | |
checkVer = true; | |
break; | |
} | |
el0 = document.getElementById('0' + x.toString()).className; | |
el1 = document.getElementById('1' + x.toString()).className; | |
el2 = document.getElementById('2' + x.toString()).className; | |
if (el0 === red && el1 === red && el2 === red) { | |
checkHor = true; | |
break; | |
} else if (el0 === blue && el1 === blue && el2 === blue) { | |
checkHor = true; | |
break; | |
} | |
} | |
/* Check for diagonal win conditions */ | |
let ul = document.getElementById('00').className; | |
let ur = document.getElementById('20').className; | |
let c = document.getElementById('11').className; | |
let ll = document.getElementById('02').className; | |
let lr = document.getElementById('22').className; | |
/* On a win, let the user know the game is over, and unsub them. */ | |
if (checkHor || checkVer || (ul === red && c === red && lr === red) || (ul === blue && c === blue && lr === blue) || (ur === red && c === red && ll === red) || (ur === blue && c === blue && ll === blue)) { | |
document.getElementById('room-info').innerHTML = '<h2><b>Game over!</b></h2><br><h2>' + m.from + ' wins!</h2>'; | |
pub_nub.unsubscribe({ | |
channel: 'tictactoe_' + ROOM_ID | |
}); | |
} | |
/* Resizes the buttons so they look pretty */ | |
resizeBoxes(); | |
} | |
} | |
/* Saves the last message to check for cheaters */ | |
last = m; | |
} | |
/* Attach the event listener to all the buttons */ | |
let buttons = document.querySelectorAll(".row .btn"); | |
for (let i = 0; i < buttons.length; i++) { | |
buttons[i].addEventListener('click', function buttonChecked(e) { | |
/* Prevent link from being navigated to */ | |
e.preventDefault(); | |
/* Determine which button was actually clicked */ | |
let el = e.target; | |
/* Send a message to let the other player's browser know */ | |
pub_nub.publish({ | |
channel: 'tictactoe_' + ROOM_ID, | |
message: { | |
type: 'pressed', | |
node: el.id, | |
from: xo | |
} | |
}); | |
}, false); | |
} | |
/* Return public interface so it can be used outside of the IIFE */ | |
return { | |
pn: pub_nub | |
}; | |
})(); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment