<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Reactive Programming</title>
</head>
<body>
<div>
<h1>Links</h1>
<div>
<a href="http://reactivex.io/documentation/operators.html" target="_blank">Operator Decision Tree (scroll down)</a>
</div>
<div>
<a href="https://github.com/Reactive-Extensions/RxJS/tree/master/doc">RxJS 4</a>
</div>
</div>
<div>
<h1>Exercise: Input</h1>
<div>
Check the input field
</div>
<div id="helloWorld">
<input type="text" id="input1">
<button id="stopLogging">Stop logging</button>
<br>
<input type="text" id="input2" disabled="true"> Reacts to input with more than 2 characters
<br>
<input type="text" id="input3" disabled="true"> Reacts to input after a short delay
<br>
<input type="text" id="input4" disabled="true"> Reacts to the first 3 events. Then stops
</div>
</div>
<div>
<h1>Exercise:Arrays & Objects</h1>
<div>
Try sort, map, group by and filtering
</div>
<div id="arraysObjects">
<!-- The content for this will be generated dynamicaly -->
</div>
</div>
<div>
<h1>Exercise: Chat</h1>
your name :
<input type="text" name="" id="chatUsername" value="" />
<button id="chatLoginButton">Login</button>
<div id="chatMessages" style="height:200px; overflow:auto; border: 1px solid black;"></div>
<div id="chatCurrentlyTyping"></div>
<form action="" id="chatForm">
<input id="chatInput" autocomplete="off" />
<button id="chatSendButton" disabled="true">Send</button>
</form>
<div>
All Users:
<div id="chatAllUsers"></div>
</div>
</div>
<div>
<h1>Exercise: Typing of the Dead</h1>
<div>
You get a point every time you type the word correctly. If you make a typo you lose all your points!
</div>
<div id="TotDWord"></div>
<input type="text" name="" id="TotDInput" value="" /><span>Highscore: </span><span id="TotDHighscore"></span>
<div id="TotDScore"></div>
</div>
<div>
<h1>Tweet Service</h1>
<div>
Get the newest infos straight from twitter!
</div>
<div id="tweetService">
<!-- The content for this will be generated dynamicaly -->
</div>
</div>
<div>
<h1>Wikipedia Suggestions</h1>
<div id="wikipedia">
<input type="text" name="" id="wikipediaInput" value="" />
<div id="wikipediaOutput"></div>
</div>
</div>
<div>
<h1>Multiclick</h1>
<div id="multiclick">
<button id="multiclickButton">Click me fast!</button>
</div>
</div>
<script>
console.clear();
</script>
<script data-require="jquery" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
<script src="util/renderer.js"></script>
<script src="references/3_1-TweetUI.js"></script>
<script src="references/3_1-TweetService.js"></script>
<script src="references/3_1-TweetApp.js"></script>
<script src="references/3_2-Wikipedia.js"></script>
<script src="references/3_3-Multiclick.js"></script>
<script src="exercise/Input.js"></script>
<script src="exercise/ArraysObjects.js"></script>
<script src="exercise/ChatClient.js"></script>
<script src="exercise/TypingOfTheDead.js"></script>
</body>
function render(data, containerId) {
if (!containerId) containerId = 'content';
const content = document.createElement('div');
innerHtml = data;
if (typeof data === 'object') innerHtml = JSON.stringify(data);
content.innerHTML = innerHtml;
document.getElementById(containerId).appendChild(content);
}
function renderHtml(html, containerId) {
if (!containerId) containerId = 'content';
document.getElementById(containerId).appendChild(html);
}
function createElement(containerId) {
const ele = document.createElement('div');
renderHtml(ele, containerId);
return $(ele);
}
(() => {
const hwInput = $('#input1')
const hwOutputFilter = $('#input2')
const hwOutputDelay = $('#input3')
const hwOutputTake3 = $('#input4')
const stopLoggingButton = $('#stopLogging');
var input = Rx.Observable.fromEvent(hwInput, 'input')
.map(event => event.target.value);
// Filter out target values less than 3 characters long
input.filter(text => text.length > 2)
.subscribe(value => hwOutputFilter.val(value)); // "hel"
// Delay the events
input.delay(200)
.subscribe(value => hwOutputDelay.val(value)); // "h" -200ms-> "e" -200ms-> "l" ...
// Stop the stream of events after 3 events
input.take(3)
.subscribe(value => hwOutputTake3.val(value)); // "hel"
// Passes through events until other observable triggers an event
var stopStream = Rx.Observable.fromEvent(stopLoggingButton, 'click');
input.takeUntil(stopStream)
.subscribe(value => console.log(value)); // "hello" (click)
}
)();
{
"myData":"got loaded"
}
class TweetService {
constructor() {
// Subject is an implementation of an Observable that allows us to manually emit an event via onNext()
this.updateStream = new Rx.Subject();
// The tweetStream fetches new data, everytime the updateStream emits an event
this.tweetStream = this.updateStream.flatMapLatest(this.fetchData);
}
// Returns a promise. Only the caller of the function will receive the new data
getDataPromise() {
return this.fetchData();
}
// Every caller of this function gets the same observable and therefore all of the updates
getDataObservable() {
return this.tweetStream;
}
// Trigger an update for the observable
updateDataForObservable() {
// Send an event to the update stream - this will cause the tweetStream to call the server
this.updateStream.onNext();
}
// Make an HTTP call to get the data
// Returns a promise
fetchData() {
// We get mock data instead of calling an actual server
return $.get('mockData.json');
}
}
// This class renders our tweets
class TweetDisplayer {
constructor(tweetService) {
this.service = tweetService;
this.service.getDataObservable()
.subscribe(tweets => {
const data = 'At ' + new Date().toLocaleTimeString() + ' Trump tweeted something stupid!';
render(data, 'tweetService');
});
// Creates a button that calls the update function on click
createTweetUiButton('Update from the displayer class', () => this.update());
}
update() {
this.service.updateDataForObservable();
}
}
// This is some other class that can also trigger an update for Tweets - and the TweetDisplayer needs to react to these aswell
class AnotherClass {
constructor(tweetService) {
this.service = tweetService;
// Creates a button that calls the update function on click
createTweetUiButton('Update from another class', () => this.update());
}
update() {
this.service.updateDataForObservable();
}
}
function createTweetUiButton(text, onClick) {
const button = document.createElement('button');
button.innerHTML = text;
button.onclick = onClick;
renderHtml(button, 'tweetService');
}
(() => {
// Create the service, displayer and another class. It's important that all UI elements get the same instance of the service!
const service = new TweetService();
const displayer = new TweetDisplayer(service);
const otherThing = new AnotherClass(service);
})();
(() => {
const myInputField = $('#wikipediaInput');
const suggestionBox = $('#wikipediaOutput');
const inputs = Rx.Observable.fromEvent(myInputField, 'input')
.map(e => e.target.value);
const newSearchTerm = inputs
.filter(text => text.length > 2)
.throttle(500);
newSearchTerm.flatMapLatest(searchWikipedia)
.subscribe(renderData, renderError);
// Queries the wikipedia servers
// returns a promise
function searchWikipedia(term) {
return $.ajax({
// Change the URL to cause a 404 error
url: 'https://en.wikipedia.org/w/api.php',
dataType: 'jsonp',
data: {
action: 'opensearch',
format: 'json',
search: term
}
}).promise();
}
// ------------- RENDER FUNCTIONS ------------------
// A function that displays the data in the UI - no need to know how it works
function renderData(data) {
var res = data[1];
suggestionBox.empty();
$.each(res, (_, value) => $('<li>' + value + '</li>').appendTo(suggestionBox));
}
function renderError(error) {
suggestionBox.empty();
$('<li>Error: ' + error.status + '</li>').appendTo(suggestionBox);
}
})();
(() => {
const multiButton = $('#multiclickButton');
const clickStream = Rx.Observable.fromEvent(multiButton, 'click');
const multiClicks = clickStream
.buffer(clickStream.debounce(250))
.map(x => x.length)
.filter(x => x>=2);
const messageBox = createElement('multiclick');
multiClicks.subscribe(x => {
messageBox.empty();
messageBox.append( x + ' Clicks!');
});
})();
/*******************************************************************************
* Recommended order:
* Send/receive message
* Display current users
* "Is Typing"
* Send/receive PM (private message)
******************************************************************************/
(() => {
// ----- UI Elements -----
const loginButton = $('#chatLoginButton');
const usernameInput = $('#chatUsername');
const messages = $('#chatMessages');
const input = $('#chatInput');
const form = $('#chatForm');
const messageInput = $('#chatInput');
// Box to display who's currently typing
const currentlyTyping = $('#chatCurrentlyTyping');
// Box to display all current users
const allUsers = $('#chatAllUsers');
var username;
// ----- Login ---------------------------------------------------------------
loginButton.click(() => {
username = usernameInput.val();
if (!username) {
alert('Please enter a username');
return;
}
if(username.indexOf(' ') !== -1){
alert('No spaces allowed in the username');
return;
}
connect(username);
loginButton.prop('disabled', true);
usernameInput.prop('disabled', true);
$('#chatSendButton').prop('disabled', false);
});
// ----- Chat ----------------------------------------------------------------
function connect(username) {
// ----- Connect to the server -----
var socket = io.connect('http://141.60.170.61:3000', {
transports: ['websocket'],
query: 'name=' + username
});
// ----- Send message -----
form.submit(() => {
const message = input.val();
if (!message) return false;
/**
* TODO: Send Message
* Look at the ChatServer code to see how socket.io works.
* You will use socket.emit(...) and socket.on(...) to interact with the server
* Check the server API file in this plunker for infos
*/
/**
* TODO: Send PMs
* Use the isPM function to check if a string conforms to the PM syntax
* Use the createPM function to turn the user input string to an PM object
*/
input.val('');
return false;
});
// ----- Receive message -----
/**
* TODO
* Use the renderMessage function to display them
*/
// ----- Receive PM -----
/**
* TODO
* Use the pmToMe and pmFromMe functions to render the PMs
* Check the Server API for infos on the data model
*/
// ----- "Is Typing" -----
/**
* TODO
* Send a signal, when you are typing (don't worry about sending the same signal multiple times)
* Send a signal, when you haven't typed anything for 3 seconds
* Use the renderIsTyping function to display the names
*/
// ----- Display current users -----
/**
* TODO
* Use the renderUsers function to display the names
*/
}
// ----- Render functions ----------------------------------------------------
// Renders an array of names of people who are typing
function renderIsTyping(names) {
currentlyTyping.empty();
if (!names.length) return;
currentlyTyping.append(names.join(', ') + ' is typing...');
}
// Renders an array of names in the all users section
function renderUsers(users) {
allUsers.empty();
allUsers.append(users.join('<br>'))
}
// Render a string in the message box
function renderMessage(msg) {
render(msg, 'chatMessages')
var foo = document.getElementById('chatMessages');
foo.scrollTop = foo.scrollHeight;
}
// Render a PM object sent from someone to me
function pmToMe(msg) {
renderMessage(`From ${msg.from}: ${msg.message}`);
}
// Render a PM object sent from me to someone
function pmFromMe(msg) {
renderMessage(`to ${msg.to}: ${msg.message}`);
}
// ----- Regex functions -----------------------------------------------------
// PMs look like this
// "/pm" + username (no spaces) + message
// "/pm Manuel Hey man, what's up?"
const pmRegex = /\/pm\s+(\w+)\s+(.*)/;
// returns true if the name is a PM.
function isPM(s) {
return s.match(pmRegex);
}
// Creates a PM object from a string that isPM()
// Returns a object with a "to" and "message" property (which is what the server expects)
function createPM(s) {
const captureGroups = s.match(pmRegex);
return {
to: captureGroups[1],
message: captureGroups[2]
};
}
})();
(() => {
// --- UI Stuff, NO NEED TO TOUCH THESE --- //
const wordField = $('#TotDWord');
const inputField = $('#TotDInput');
const scoreField = $('#TotDScore');
const highscoreField = $('#TotDHighscore');
// ----------------------------------------- //
let score = 0;
let highscore = 0;
/**
* TODO: create a stream from the input field, that returns the user's input as string
* Hint: See the Wikipedia example
*/
// const inputStream = ???
// A stream that allows us to manually trigger that we need a new word
// To make a Subject emit a new value call .onNext() function with the value (or nothing) as parameter.
// Example: nextStream.onNext()
const nextStream = new Rx.Subject();
// When we want a new word we need to reset the users input
nextStream.subscribe(() => {
// Setting the value doesn't trigger a change event
inputField.val('');
// so we need to manually trigger the event
inputField.trigger('input');
});
/**
* TODO: Complete the statement below to create a stream that emits a random word everytime the nextStream emits an event.
* The .startWith() is needed to load the first word
* Look at the Wikipedia example on how to handle a function that returns a promise. Use the "getRandomWord" function defined at the end of this file.
* Call ".share()" after the map function to cache the result and avoid multiple server calls
*/
// const wordStream = nextStream.startWith('').???
/**
* TODO: Uncomment this code when you have the word stream
* A random word should be displayed above the input box then.
* If there is none, check the wordStream or ask for help.
*/
// When there is a new word, we display it
// wordStream.subscribe(word => {
// wordField.empty();
// wordField.append(word);
// });
/**
* TODO: combine the latest values from wordStream and the inputStream.
* Every time either of those stream emits a value this stream should emit a tuple with the latest values of both streams.
* The tuple need to look like this: ['the word', 'the user input']
* Call .share() after combining the stream to make sure every subscriber receives the same values - this is a bit more advanced so ask for help if you want to know why
*/
// const checkStream = ???;
// Uncomment this code to see if the output of the checkStream looks like it's supposed to:
// If the word in the console doesn't match the word on the page you missed the caching in the wordStream!
// checkStream.subscribe(x => console.log(x));
/**
* TODO: Create a typoStream that emits an event everytime the user makes a mistake
* Hint:
* You can access the values of tuples with the index notation "myTupleVariableName[0]" and "myTupleVariableName[1]"
* strings offer a ".startsWith(x)" function.
*/
// const typoStream = ???
/**
* TODO: Feed the typoStream into the nextStream
* Now every typo should cause your input to reset & a new word to appear
*/
/**
* TODO: Create a stream that emits an event every time the user has entered the entire word correctly.
*/
// const wordCompletedStream = ???
/**
* TODO: Feed the wordCompleteStream into the nextStream
* Hint: Check the comment above the nextStream to learn how to make the stream emit a value
* Now every time you complete a word it should cause your input to reset & a new word to appear
*/
// When this stream emits "true" increase the score, when it emits "false" reset to zero
const scoreUpdateStream = new Rx.Subject();
// Increase or reset score - depending on the update sent
scoreUpdateStream.subscribe(increase => score = increase ? score + 1 : 0);
/**
* TODO: Create the score stream that emits the current score whenever there is a score update.
* If the scoreUpdateStream emits false set the score to zero
* If the scoreUpdateStream emits true increase the score by 1
* Use the variable "score" to persist the current score.
* Make the scoreStream startWith (= emit from the beginngin) a value of 0
*/
// const scoreStream = ???
/**
* TODO: Uncomment once you have the scoreStream
*/
//scoreStream.subscribe(num => {
// scoreField.empty();
// scoreField.append(num);
//});
/**
* TODO: Update the score
* On a typo, reset the score
* On a completed word increase the score
* Hint: send values to the scoreUpdateStream
*/
/**
* TODO: Create a highscoreStream that emits the new highscore everytime the old one has been broken
* Hint: just assume the current highscore is in the "highscore" variable - we will fill it later
*/
// const highscoreStream = ???
/**
* TODO: Uncomment once you have the highscoreStream
* Now you should have a working highscore field next to the input field
*/
//highscoreStream.subscribe(num => {
// highscore = num;
// highscoreField.empty()
// highscoreField.append(num);
//});
/**
* YOURE DONE!
* Make sure all of the following are correct:
* There's a word at the start.
* When you type a word correctly the input field empties, a new word is generated and you get one point
* When you make a typo the input field empties, a new word is generated and you lose all points
* Your highscore is updated every time you have a new highscore
*/
// Calls a server for a random word
// returns a promise
function getRandomWord() {
return $.ajax({
// Change the URL to cause a 404 error
url: 'http://setgetgo.com/randomword/get.php'
}).promise();
}
})();
(() => {
// --- UI Stuff, NO NEED TO TOUCH THESE --- //
const wordField = $('#TotDWord');
const inputField = $('#TotDInput');
const scoreField = $('#TotDScore');
const highscoreField = $('#TotDHighscore');
// ----------------------------------------- //
let score = 0;
let highscore = -1;
// A stream of the users string inputs
const inputStream = Rx.Observable.fromEvent(inputField, 'input')
.map(x => x.target.value);
// A stream that allows us to manually trigger that we need a new word
const nextStream = new Rx.Subject();
// When we want the next word we need to reset the users input
nextStream.subscribe(() => inputField.val('').trigger('input'));
// This stream calls a server for a new random word every time the nextStream emits an event. We startWith a value to trigger the first word
const wordStream = nextStream.startWith('')
.flatMapLatest(getRandomWord)
// share() to cache the result - otherwise every .map on wordStream would cause a new HTTP request (and therefore another random word)
.share();
// When there is a new word, we display it
wordStream.subscribe(word => {
wordField.empty();
wordField.append(word);
});
// Checkstream combines the latest word with the latest userinput. It emits an array, like this ['the word', 'the user input'];
// The share() is to make sure everybody receives the same data tuple (like the wordStream caching)
const checkStream = wordStream.combineLatest(inputStream).share();
// Emits an event if the user input is not correct
const typoStream = checkStream.map(tuple => {
const word = tuple[0];
const input = tuple[1];
return word.startsWith(input);
})
.filter(x => !x);
// When there is a typo we need a new word
typoStream.subscribe(nextStream);
// This is a short version for
// typoStream.subscribe(x => nextStream.onNext(x));
// Emits an event when the user has entered the entire word correctly
const wordCompletedStream = checkStream.filter(tuple => {
const word = tuple[0];
const input = tuple[1];
return word == input;
});
typoStream.subscribe(x => console.log('wont get executed'));
// Whenever the word is completed, request a new word
wordCompletedStream.subscribe(nextStream);
typoStream.subscribe(x => console.log('will get executed'));
// When this stream emits "true" increase the score, when it emits "false" reset to zero
const scoreUpdateStream = new Rx.Subject();
scoreUpdateStream.subscribe(increase => {
const newScore = increase ? ++score : 0;
score = newScore;
});
const scoreStream = scoreUpdateStream.map(increase => score).startWith(0);
scoreStream.subscribe(num => {
scoreField.empty();
scoreField.append(num);
});
typoStream.subscribe(() => scoreUpdateStream.onNext(false));
wordCompletedStream.subscribe(() => scoreUpdateStream.onNext(true))
const highscoreStream = scoreStream.filter(x => x > highscore);
highscoreStream.subscribe(num => {
highscore = num;
highscoreField.empty()
highscoreField.append(num);
});
// Calls a server for a random word
// returns a promise
function getRandomWord() {
return $.ajax({
// Change the URL to cause a 404 error
url: 'http://setgetgo.com/randomword/get.php'
}).promise();
}
})();
(() => {
// ----- UI Elements -----
const loginButton = $('#chatLoginButton');
const usernameInput = $('#chatUsername');
const messages = $('#chatMessages');
const input = $('#chatInput');
const form = $('#chatForm');
const messageInput = $('#chatInput');
// Box to display who's currently typing
const currentlyTyping = $('#chatCurrentlyTyping');
// Box to display all current users
const allUsers = $('#chatAllUsers');
// ----- Login ---------------------------------------------------------------
loginButton.click(() => {
const username = usernameInput.val();
if (!username) {
alert('Please enter a username');
return;
}
if (username.indexOf(' ') !== -1) {
alert('No spaces allowed in the username');
return;
}
connect(username);
loginButton.prop('disabled', true);
usernameInput.prop('disabled', true);
$('#chatSendButton').prop('disabled', false);
});
// ----- Chat ----------------------------------------------------------------
function connect(username) {
// ----- Connect to the server -----
var socket = io.connect('http://141.60.170.61:3000', {
transports: ['websocket'],
query: 'name=' + username
});
// ----- Send message -----
form.submit((e) => {
const message = input.val();
if (!message) return false;
if (isPM(message)) {
socket.emit('pm', createPM(message));
} else {
socket.emit('chat message', message);
}
input.val('');
e.preventDefault();
e.stopPropagation();
return false;
});
// ----- Receive message -----
socket.on('chat message', renderMessage);
// ----- Receive PM -----
socket.on('pm', pm => {
if(pm.from === username) pmFromMe(pm)
else pmToMe(pm)
});
// ----- "Is Typing" -----
const inputStream = Rx.Observable.fromEvent(messageInput, 'keyup')
.map(x => x.target.value);
inputStream.subscribe(x => socket.emit('start typing'));
inputStream.buffer(inputStream.debounce(3000))
.subscribe(x => socket.emit('stop typing'));
socket.on('typing', renderIsTyping);
// ----- Display current users -----
socket.on('all users', renderUsers);
}
// ----- Render functions ----------------------------------------------------
function renderIsTyping(names) {
currentlyTyping.empty();
if (!names.length) return;
currentlyTyping.append(names.join(', ') + ' is typing...');
}
function renderUsers(users) {
allUsers.empty();
allUsers.append(users.join('<br>'))
}
function renderMessage(msg) {
render(msg, 'chatMessages')
var foo = document.getElementById('chatMessages');
foo.scrollTop = foo.scrollHeight;
}
function pmToMe(msg) {
renderMessage(`From ${msg.from}: ${msg.message}`);
}
function pmFromMe(msg) {
renderMessage(`to ${msg.to}: ${msg.message}`);
}
// ----- Regex functions -----------------------------------------------------
// PMs look like this
// "/pm" + username (no spaces) + message
// "/pm Manuel Hey man, what's up?"
const pmRegex = /\/pm\s+(\w+)\s+(.*)/;
// returns true if the name is a PM.
function isPM(s) {
return s.match(pmRegex);
}
// Creates a PM object from a string that isPM()
// Returns a object with a "to" and "message" property (which is what the server expects)
function createPM(s) {
const captureGroups = s.match(pmRegex);
return {
to: captureGroups[1],
message: captureGroups[2]
};
}
})();
Chat server receives events:
name: 'chat message'
description: Send a message to the chat. Everybody will receive it
data: string
name: 'start typing'
description: Send this to the server when you start typing
data: no data needs to be sent
name: 'stop typing'
description: Send this to the server when you stop typing
data: no data needs to be sent
name: 'pm'
description: Send this to the server to send a message to only one person
data: Object
properties: "to" string, "message" string
example: {to: "Peter", message: "Hi!"}
Chat server emits events:
name: 'chat message'
description: The server send a message to everybody connected. The string will be prefixed with the name of the sender
data: string
name: 'pm'
description: This event is sent to the sender and receiver of a PM
data: Object
properties: "from" string, "to" string, "message" string
example: {from: "Markus", to: "Peter", message: "Hey, what's up dude?"}
name: 'typing'
description: Emits an array of all users that are currently typing
data: Array of strings
example: ['Manuel', 'Sonja']
name: 'all users'
description: Emits an array of all users that are currently online
data: Array of strings
example: ['Manuel', 'Sonja']
/*******************************************************************************
* THIS CODE IS ONLY A REFERENCE. THE CHAT SERVER DOESN'T ACTUALLY RUN IN YOUR
* BROWSER. CHANING CODE HERE WON'T DO ANYTHING AT ALL.
******************************************************************************/
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const port = process.env.PORT || 3000;
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
const allConnections = {};
const currentlyTyping = [];
io.on('connection', function (socket) {
const name = socket.request._query['name'];
allConnections[name] = socket;
emitUsers();
console.log(name + ' connected');
socket.on('disconnect', function () {
console.log(name + ' disconnected');
delete allConnections[name];
emitUsers();
});
socket.on('chat message', function (msg) {
const message = name + ': ' + msg;
console.log(message);
io.emit('chat message', message);
});
socket.on('start typing', function () {
if (currentlyTyping.indexOf(name) !== -1) return;
currentlyTyping.push(name);
emitTyping();
});
socket.on('stop typing', function () {
if (currentlyTyping.indexOf(name) === -1) return;
currentlyTyping.splice(currentlyTyping.indexOf(name), 1);
emitTyping();
});
socket.on('pm', function (pm) {
if (!pm || !allConnections[pm.to] || pm.to === name) return;
console.log(`${name} to ${pm.to}: ${pm.message}`);
let id = allConnections[pm.to].id;
pm.from = name;
socket.to(id).emit('pm', pm);
socket.emit('pm', pm);
});
function emitTyping() {
io.emit('typing', currentlyTyping);
}
function emitUsers() {
io.emit('all users', Object.keys(allConnections));
}
});
http.listen(port, function () {
console.log('listening on *:' + port);
});
(() => {
const aoOutputArray = document.createElement('input');
aoOutputArray.disabled = true;
renderHtml(aoOutputArray, 'arraysObjects');
const hwBreakAO = document.createElement('br');
renderHtml(hwBreakAO, 'arraysObjects');
const aoOutputObject = document.createElement('textarea');
/**************************************FIRST TASK*****************************************************/
const myMixedArray = new Array("&", 1, 2, "n", 3, 4, 5, 12, 'u', 10, "r", 11, 6, 7, "Z", 8, "e", 9, "i", "c", "h", "e", "n");
//TODO create Observable with source myMixedArray
//TODO sort all characters of myMixedArray
// use for output: .subscribe(value => addValue(value));
/**************************************SECOND TASK*****************************************************/
//Second task -
const people = [{name: 'Anna', age: 25, city: 'Rosenheim'},{name: 'Johann', age: 34 , city: 'Bad Endorf'},
{name: 'Max', age: 26, city: 'Bad Aibling'}, {name: 'Sarah', age: 35, city: 'Rosenheim'},
{name: 'Klaus', age: 22, city: 'Bad Aibling'}, {name: 'Alfons', age: 21, city: 'Rosenheim'},
{name: 'Stefanie', age: 28, city: 'Bad Aibling'}, {name: 'Michael', age: 27, city: 'München'},
{name: 'Peter', age: 29, city: 'Traunstein'}, {name: 'Anna', age: 23, city: 'Rosenheim'}];
//TODO create Observable with source myMixedArray
//TODO group the following people by city and select those which are 25 or older
// use for output: .subscribe(val => outputObject( val));
/********************************HELPER FUNCTION******************************************************************/
function addValue(value) {
aoOutputArray.setAttribute("value", (aoOutputArray.getAttribute("value") + " " + value));
}
function outputObject(value) {
let data = aoOutputObject.getAttribute("value");
function getData(arr) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].length !== undefined && arr[i].length > 0) {
getData(arr[i]);
} else {
if (arr[i] !== null && arr[i] != 0) {
console.log(arr[i])
$('<li>' + arr[i].name + " lives in " + arr[i].city + '</li>').appendTo(arraysObjects);
}
}
}
}
getData(value);
}
}
)();
(() => {
// ----- UI Elements -----
const hwInput = $('#input1')
const hwOutputFilter = $('#input2')
const hwOutputDelay = $('#input3')
const hwOutputTake3 = $('#input4')
const stopLoggingButton = $('#stopLogging');
const hwOutputStop = $('#input5');
var input = Rx.Observable.fromEvent(hwInput, 'input')
.map(event => event.target.value);
//TODO Filter the input and show only characters that are longer than 3 characters
// use to display .subscribe(value => hwOutputFilter.val(value));
//TODO Delay the events by 200ms
// use to display .subscribe(value => hwOutputDelay.val(value));
// TODO Stop the stream of events after 3 events
// use to display .subscribe(value => hwOutputTake3.val(value));
/*TODO create a new observable based on the stop button,
when the button is clicked, the output into hwOutputStop should end
*/
// use to display .subscribe(value => hwOutputStop.val(value));
}
)();