diff --git a/app/main/chat.php b/app/main/chat.php
new file mode 100644
index 0000000..ba07d4d
--- /dev/null
+++ b/app/main/chat.php
@@ -0,0 +1,45 @@
+clients = new \SplObjectStorage;
+ echo "__construct() \n";
+ }
+
+ public function onOpen(ConnectionInterface $conn) {
+ //store the new connection
+ $this->clients->attach($conn);
+ echo "NEW connection! ({$conn->resourceId})\n";
+ }
+
+ public function onMessage(ConnectionInterface $from, $msg) {
+ $numRecv = count($this->clients) - 1;
+ echo sprintf('Connection %d sending to %d other connection%s' . "\n"
+ , $from->resourceId, $numRecv, $numRecv == 1 ? '' : 's');
+
+ foreach ($this->clients as $client) {
+ // if ($from !== $client) {
+ $client->send($msg);
+ // }
+ }
+ }
+
+ public function onClose(ConnectionInterface $conn) {
+ $this->clients->detach($conn);
+
+ echo "Connection {$conn->resourceId} has disconnected\n";
+ }
+
+ public function onError(ConnectionInterface $conn, \Exception $e) {
+ echo "An error has occurred: {$e->getMessage()}\n";
+
+ $conn->close();
+ }
+}
\ No newline at end of file
diff --git a/app/websockets.php b/app/websockets.php
new file mode 100644
index 0000000..9c27f55
--- /dev/null
+++ b/app/websockets.php
@@ -0,0 +1,34 @@
+startChat();
+ }
+
+ private function startChat(){
+ $server = IoServer::factory(
+ new HttpServer(
+ new WsServer(
+ new Main\Chat()
+ )
+ ),
+ 8020
+ );
+
+ $server->run();
+ }
+
+}
\ No newline at end of file
diff --git a/cmd.php b/cmd.php
new file mode 100644
index 0000000..f01c96a
--- /dev/null
+++ b/cmd.php
@@ -0,0 +1,6 @@
+
+
+
+
+ Chat
+
+
+
+
+
+
+
+
+ Status:
●
+ Notification:
●
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/app.js b/js/app.js
new file mode 100644
index 0000000..ab9d269
--- /dev/null
+++ b/js/app.js
@@ -0,0 +1,155 @@
+window.onload = function (){
+
+ var msgWorker = this.msgWorker;
+
+ var worker = new SharedWorker('js/worker/chat_new.js', 'worker_name');
+
+ worker.port.addEventListener('message', function(e){
+ let load = e.data;
+ load.__proto__ = msgWorker.prototype;
+
+ switch(load.command){
+ case 'ws:open':
+ // WebSocket in SharedWorker is open
+ setSocketStatus(true);
+ initChat();
+ break;
+ case 'ws:send':
+ updateMessages(load.data());
+ break;
+ case 'ws:closed':
+ setSocketStatus(false);
+ break;
+ }
+ }, false);
+
+ worker.onerror = function(e){
+ console.error('SharedWorker onerror:');
+ };
+
+ worker.port.start();
+
+ var msgWorkerInit = new msgWorker('ws:init');
+ msgWorkerInit.data({
+ uri: 'ws://wstest.local:8080'
+ });
+ worker.port.postMessage(msgWorkerInit);
+
+
+ // Chat init ==================================================================================
+ var user;
+ var messages = [];
+
+ var messages_template = Handlebars.compile($('#messages-template').html());
+
+ var initChat = function(){
+
+ $('#join-chat').click(function(){
+ user = $('#user').val();
+ $('#user-container').addClass('hidden');
+ $('#main-container').removeClass('hidden');
+
+ var msgWorkerSend = new msgWorker('ws:send');
+ msgWorkerSend.data({
+ 'user': user,
+ 'text': user + ' entered the room',
+ 'time': moment().format('hh:mm a')
+ });
+
+ worker.port.postMessage(msgWorkerSend);
+
+ $('#user').val('');
+ });
+
+ $('#send-msg').click(function(){
+ var text = $('#msg').val();
+
+ var msgWorkerSend = new msgWorker('ws:send');
+ msgWorkerSend.data({
+ 'user': user,
+ 'text': text,
+ 'time': moment().format('hh:mm a')
+ });
+
+ worker.port.postMessage(msgWorkerSend);
+
+ $('#msg').val('');
+ });
+
+ $('#leave-room').click(function(){
+ var msgWorkerSend = new msgWorker('ws:send');
+ msgWorkerSend.data({
+ 'user': user,
+ 'text': user + ' has left the room',
+ 'time': moment().format('hh:mm a')
+ });
+
+ worker.port.postMessage(msgWorkerSend);
+
+ $('#messages').html('');
+ messages = [];
+
+ $('#main-container').addClass('hidden');
+ $('#user-container').removeClass('hidden');
+
+
+ });
+ };
+
+ var setSocketStatus = function(status){
+ $('#socket-status').toggleClass('red', !status).toggleClass('green', status);
+ };
+
+ var updateMessages = function(msg){
+ messages.push(msg);
+ var messages_html = messages_template({'messages': messages});
+ $('#messages').html(messages_html);
+ $("#messages").animate({ scrollTop: $('#messages')[0].scrollHeight}, 1000);
+ };
+
+ // Notification init ==========================================================================
+
+ var updateNotification = function(status){
+ $('#notification-status').toggleClass('red', !status).toggleClass('green', status);
+ };
+
+ var notifyMe = function(){
+ var msgWorkerNotify = new msgWorker('ws:notify');
+
+ if (Notification.permission === 'granted'){
+ msgWorkerNotify.data({
+ status: true
+ });
+ worker.port.postMessage(msgWorkerNotify);
+
+ updateNotification(true);
+ }else{
+ Notification.requestPermission(function (permission) {
+ msgWorkerNotify.data({
+ status: permission === 'granted'
+ });
+ worker.port.postMessage(msgWorkerNotify);
+
+ updateNotification(permission === 'granted');
+ });
+ }
+ };
+
+ $('#toggle-notification').on('click', notifyMe);
+
+ // ============================================================================================
+ /*
+ window.onbeforeunload = function() {
+ var msgWorkerClose = new msgWorker('ws:close');
+ worker.port.postMessage(msgWorkerClose);
+
+ //console.log('test close');
+ //worker.port.close();
+
+ return 'sdf';
+ };
+ */
+};
+
+
+
diff --git a/js/worker/chat.js b/js/worker/chat.js
new file mode 100644
index 0000000..767c0db
--- /dev/null
+++ b/js/worker/chat.js
@@ -0,0 +1,87 @@
+'use strict';
+self.importScripts('message.js');
+
+var socket = new WebSocket(self.name);
+var ports = [];
+var notifications = false;
+var tmp = self;
+console.log(socket._socket);
+
+self.addEventListener('connect', function (event) {
+ var port = event.ports[0];
+ ports.push(port);
+console.log('B: ' + socket.readyState);
+ port.onmessage = function (event) {
+ let load = event.data;
+ load.__proto__ = msgWorker.prototype;
+
+console.log('C: ' + socket.readyState);
+
+ switch(load.command){
+ case 'send':
+ socket.send(JSON.stringify(load.data()));
+ break;
+ case 'WS_close':
+ closeSocket(socket);
+ break;
+ case 'notify':
+ notifications = load.data().status;
+ }
+ };
+
+
+ if(socket.readyState === socket.OPEN){
+ var msgWorkerOpen = new msgWorker('ready');
+ port.postMessage(msgWorkerOpen);
+ }
+
+ port.start();
+}, false);
+
+
+socket.onopen = function(e){
+
+ var msgWorkerOpen = new msgWorker('open');
+ for (var i = 0; i < ports.length; i++) {
+ ports[i].postMessage(msgWorkerOpen);
+ }
+
+ socket.onmessage = function(e){
+ let load = JSON.parse(e.data);
+ var msgWorkerSend = new msgWorker('send');
+ msgWorkerSend.data(load);
+
+ for (var i = 0; i < ports.length; i++) {
+ ports[i].postMessage(msgWorkerSend);
+ }
+
+ if(notifications){
+ new Notification('Message: ' + load.text);
+ }
+ };
+
+ socket.onclose = function(){
+
+ console.log(this.remoteAddress);
+ console.info('ws: onclose()');
+ };
+
+ socket.onerror = function(){
+ console.error('ws: onerror()');
+ };
+
+};
+
+// Util ================================================================
+var closeSocket = function(socket){
+ // only close if active
+ if(socket.readyState === socket.OPEN){
+ // send "close" event before close call
+ var msgWorkerWsClosed = new msgWorker('WS_closed');
+ for (var i = 0; i < ports.length; i++) {
+ ports[i].postMessage(msgWorkerWsClosed);
+ }
+
+ socket.close();
+ }
+};
diff --git a/js/worker/chat_new.js b/js/worker/chat_new.js
new file mode 100644
index 0000000..d0e167b
--- /dev/null
+++ b/js/worker/chat_new.js
@@ -0,0 +1,90 @@
+'use strict';
+self.importScripts('message.js');
+
+var socket = null;
+var ports = [];
+var notifications = false;
+
+var initSocket = function(uri){
+ var msgWorkerOpen = new msgWorker('ws:open');
+
+ if(socket === null){
+ socket = new WebSocket(uri);
+
+ socket.onopen = function(e){
+
+ //
+ ports[ports.length - 1].postMessage(msgWorkerOpen);
+
+ socket.onmessage = function(e){
+
+ let load = JSON.parse(e.data);
+ var msgWorkerSend = new msgWorker('ws:send');
+ msgWorkerSend.data(load);
+
+ for (var i = 0; i < ports.length; i++) {
+ ports[i].postMessage(msgWorkerSend);
+ }
+
+ if(notifications){
+ new Notification('Message: ' + load.text);
+ }
+ };
+
+ socket.onclose = function(){
+ console.info('ws: onclose()');
+ };
+
+ socket.onerror = function(){
+ console.error('ws: onerror()');
+ };
+ }
+ }else{
+ // socket still open
+ ports[ports.length - 1].postMessage(msgWorkerOpen);
+ }
+};
+
+self.addEventListener('connect', function (event){
+ var port = event.ports[0];
+ ports.push(port);
+
+ port.addEventListener('message', function (e){
+ let load = e.data;
+ load.__proto__ = msgWorker.prototype;
+
+ switch(load.command){
+ case 'ws:init':
+ initSocket(load.data().uri);
+ break;
+ case 'ws:send':
+ socket.send(JSON.stringify(load.data()));
+ break;
+ case 'ws:close':
+ closeSocket(socket);
+ break;
+ case 'ws:notify':
+ notifications = load.data().status;
+ break;
+ }
+ }, false);
+
+ port.start();
+}, false);
+
+
+
+// Util ================================================================
+var closeSocket = function(socket){
+ // only close if active
+ console.log(socket.readyState + ' - ' + socket.OPEN);
+ if(socket.readyState === socket.OPEN){
+ // send "close" event before close call
+ var msgWorkerWsClosed = new msgWorker('ws:closed');
+ for (var i = 0; i < ports.length; i++) {
+ ports[i].postMessage(msgWorkerWsClosed);
+ }
+
+ socket.close();
+ }
+};
diff --git a/js/worker/message.js b/js/worker/message.js
new file mode 100644
index 0000000..5527c44
--- /dev/null
+++ b/js/worker/message.js
@@ -0,0 +1,18 @@
+var msgWorker = class MsgWorkerTest {
+ constructor(cmd){
+ this.cmd = cmd;
+ this.msgBody = null;
+ }
+
+ get command(){
+ return this.cmd;
+ }
+
+
+ data(data) {
+ if(data){
+ this.msgBody = data;
+ }
+ return this.msgBody;
+ }
+};