Skip to content

Instantly share code, notes, and snippets.

@kbohinski
Last active March 18, 2016 17:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kbohinski/cb3fee469351f3a40cf5 to your computer and use it in GitHub Desktop.
Save kbohinski/cb3fee469351f3a40cf5 to your computer and use it in GitHub Desktop.
<!--
/**
* @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&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false&amp;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