diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..e586953
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,9 @@
+source 'http://rubygems.org'
+gem 'rubygems-update'
+gem 'sinatra'
+gem 'sinatra-reloader'
+gem 'sinatra-contrib'
+gem 'sinatra-websocket'
+gem 'bundler'
+gem 'activesupport'
+gem 'warden'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..5def4db
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,71 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activesupport (5.2.3)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ addressable (2.6.0)
+ public_suffix (>= 2.0.2, < 4.0)
+ backports (3.15.0)
+ concurrent-ruby (1.1.5)
+ daemons (1.3.1)
+ em-websocket (0.3.8)
+ addressable (>= 2.1.1)
+ eventmachine (>= 0.12.9)
+ eventmachine (1.2.7)
+ i18n (1.6.0)
+ concurrent-ruby (~> 1.0)
+ minitest (5.11.3)
+ multi_json (1.13.1)
+ mustermann (1.0.3)
+ public_suffix (3.1.0)
+ rack (2.0.7)
+ rack-protection (2.0.5)
+ rack
+ rubygems-update (3.0.4)
+ sinatra (2.0.5)
+ mustermann (~> 1.0)
+ rack (~> 2.0)
+ rack-protection (= 2.0.5)
+ tilt (~> 2.0)
+ sinatra-contrib (2.0.5)
+ backports (>= 2.8.2)
+ multi_json
+ mustermann (~> 1.0)
+ rack-protection (= 2.0.5)
+ sinatra (= 2.0.5)
+ tilt (>= 1.3, < 3)
+ sinatra-reloader (1.0)
+ sinatra-contrib
+ sinatra-websocket (0.3.1)
+ em-websocket (~> 0.3.6)
+ eventmachine
+ thin (>= 1.3.1, < 2.0.0)
+ thin (1.7.2)
+ daemons (~> 1.0, >= 1.0.9)
+ eventmachine (~> 1.0, >= 1.0.4)
+ rack (>= 1, < 3)
+ thread_safe (0.3.6)
+ tilt (2.0.9)
+ tzinfo (1.2.5)
+ thread_safe (~> 0.1)
+ warden (1.2.8)
+ rack (>= 2.0.6)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activesupport
+ bundler
+ rubygems-update
+ sinatra
+ sinatra-contrib
+ sinatra-reloader
+ sinatra-websocket
+ warden
+
+BUNDLED WITH
+ 2.0.2
diff --git a/app.rb b/app.rb
new file mode 100644
index 0000000..57319cc
--- /dev/null
+++ b/app.rb
@@ -0,0 +1,99 @@
+require 'bundler/setup'
+Bundler.require
+require 'sinatra/reloader' if development?
+require 'sinatra-websocket'
+require 'active_support'
+require 'active_support/core_ext'
+require 'logger'
+
+enable :sessions
+
+configure do #どの環境でも起動時に1回だけ実行される
+ set :flags => (1..6).to_a.map{|i| [i, 0]}.to_h #どのチームが押したか管理するためのハッシュ
+end
+
+set :server, 'thin'
+set :sockets, []
+
+logger = Logger.new(STDOUT)
+
+get '/' do
+ if session[:team].blank? #チームがsetされていないときに選択画面に遷移する
+ erb :select
+ else
+ pushed=Array.new
+ settings.flags.sort_by{|key,value| value}.each do |key, value|
+ if value==0
+ next
+ else
+ pushed.push("
チーム#{key}")
+ end
+ end
+ @pushed=pushed.join("\n")
+ erb :index
+ end
+end
+
+get '/websocket' do
+ if request.websocket? #WebSocket通信かどうかを確認
+ request.websocket do |ws|
+ ws.onopen do #最初に開いたときの処理
+ settings.sockets << ws
+ end
+ ws.onmessage do |btn| #ボタン or resetが押された時の処理
+ logger.debug(btn)
+ if session[:team].present? && btn=="Stop!"
+ if session[:team]>=1 && session[:team]<=6 && settings.flags[session[:team]]==0
+ settings.flags[session[:team]] = settings.flags.values.inject(:+) + 1
+ t_name="チーム#{session[:team]}"
+ logger.debug(settings.flags)
+ settings.sockets.each do |s|
+ s.send(t_name)
+ end
+ end
+ elsif session[:admin].present? && btn=="Delete!" #controllerでresetを押された時の処理
+ if session[:admin]==8804912
+ settings.sockets.each do |s|
+ settings.flags=(1..6).to_a.map{|i| [i, 0]}.to_h
+ logger.debug(settings.flags)
+ s.send("Delete!")
+ end
+ end
+ end
+ end
+ ws.onclose do #ブラウザ閉じたりとかしてWebSocket通信が切れた時の処理
+ settings.sockets.delete(ws)
+ end
+ end
+ end
+end
+
+post '/setteam' do #チーム決定ボタンを押した時の処理
+ t_num=params[:t_num].to_i
+ if t_num>=1 && t_num<=6
+ session[:team]=t_num
+ redirect "/"
+ else
+ session.clear
+ erb :error
+ end
+end
+
+get '/resetteam' do #チーム再選択ボタンを押した時の処理
+ session.clear #チーム番号を保持しているセッションを消す
+ redirect "/"
+end
+
+get '/controller' do #出題者が操作するページ
+ session[:admin]=8804912
+ pushed=Array.new
+ settings.flags.sort_by{|key,value| value}.each do |key, value|
+ if value==0
+ next
+ else
+ pushed.push("チーム#{key}")
+ end
+ end
+ @pushed=pushed.join("\n")
+ erb :controller
+end
diff --git a/public/css/main.css b/public/css/main.css
new file mode 100644
index 0000000..0e3c8f3
--- /dev/null
+++ b/public/css/main.css
@@ -0,0 +1,26 @@
+div.pushbutton {
+ text-align: center;
+}
+
+button#push {
+ width: 90%;
+ height: 80vh;
+ font-size: 1.4em;
+ font-weight: bold;
+ padding: 10px 30px;
+ color: #fff;
+ border-style: none;
+ background: #00f;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+
+button#push:hover {
+ opacity: 0.8;
+}
+
+button#push:disabled {
+color: #000;
+background:#ff0;
+}
diff --git a/public/js/client.js b/public/js/client.js
new file mode 100644
index 0000000..566a101
--- /dev/null
+++ b/public/js/client.js
@@ -0,0 +1,28 @@
+window.addEventListener('load', () => {
+ let pushedbox = document.getElementById('pushedteam');
+ let form = document.getElementById('form');
+ //let sendMsg = document.getElementById('send-msg');
+ let ws = new WebSocket('ws://' + window.location.host + '/websocket');
+
+ ws.onopen = () => console.log('connection opened');
+ ws.onclose = () => console.log('connection closed');
+ ws.onmessage = m => {
+ if (m.data=="Delete!") {
+ while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild);
+ document.getElementById('push').disabled = false;
+ } else {
+ let li = document.createElement('li');
+ li.textContent = m.data;
+ pushedbox.append(li);
+ };
+ };
+
+ //sendMsg.addEventListener('click', () => sendMsg.value = '');
+
+ form.addEventListener('submit', e => {
+ document.getElementById('push').disabled = true;
+ ws.send("Stop!"); //
+ //sendMsg.value = ''; //sendMsgを空にして初期状態に
+ e.preventDefault(); //form送信して送信先のページに遷移する動作をキャンセル
+ });
+});
diff --git a/public/js/controller.js b/public/js/controller.js
new file mode 100644
index 0000000..feca3b7
--- /dev/null
+++ b/public/js/controller.js
@@ -0,0 +1,29 @@
+window.addEventListener('load', () => {
+ let pushedbox = document.getElementById('pushedteam');
+ let ws = new WebSocket('ws://' + window.location.host + '/websocket');
+
+ ws.onopen = () => console.log('connection opened');
+ ws.onclose = () => console.log('connection closed');
+ ws.onmessage = m => {
+ if (m.data=="Delete!") {
+ while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild);
+ } else {
+ let li = document.createElement('li');
+ li.textContent = m.data;
+ pushedbox.append(li);
+ document.getElementById("hayaoshi").pause();
+ document.getElementById("hayaoshi").play();
+ playFlag = false;
+ audioObj.pause();
+ pButton.innerHTML = 'PLAY';
+ };
+ };
+
+ var resetButton = document.getElementById('reset');
+
+ resetButton.addEventListener('click', function(){
+ while (pushedbox.firstChild) pushedbox.removeChild(pushedbox.firstChild);
+ ws.send("Delete!");
+ });
+
+});
diff --git a/public/js/player.js b/public/js/player.js
new file mode 100644
index 0000000..63dcf56
--- /dev/null
+++ b/public/js/player.js
@@ -0,0 +1,65 @@
+// 再生するファイルリスト
+var fileList = [
+{ name : 'Cat_Life', url : 'cat_life.mp3' },
+{ name : 'Ray_at_midsummer', url : 'Ray_at_midsummer.mp3' }
+];
+// Audioオブジェクト
+var audioObj = new Audio();
+var playFlag = "select";
+// 再生するプレイリストを表示
+var playList = '';
+for(var i=0; i';
+}
+playList += '
';
+document.getElementById('playListArea').innerHTML = playList;
+// 再生開始
+function start_music(url, name){
+ playFlag = false;
+ audioObj.pause(); // 以前の音楽を停止させる
+ audioObj = new Audio(url);
+ audioObj.addEventListener('loadedmetadata', function() {
+ var sec = '0' + Math.floor(audioObj.currentTime % 60); // 秒数
+ var min = '0' + Math.floor(audioObj.currentTime / 60); // 分数
+ sec = sec.substr(sec.length-2, 2);
+ min = min.substr(min.length-2, 2);
+ var totalSec = '0' + Math.floor(audioObj.duration % 60); // 秒数
+ var totalMin = '0' + Math.floor(audioObj.duration / 60); // 分数
+ totalSec = totalSec.substr(totalSec.length-2, 2);
+ totalMin = totalMin.substr(totalMin.length-2, 2);
+ crtTime.innerHTML = min+":"+sec+' ['+ totalMin+':'+totalSec +']';
+ }, true);
+
+ audioObj.addEventListener('timeupdate', function(){
+ var sec = '0' + Math.floor(audioObj.currentTime % 60); // 秒数
+ var min = '0' + Math.floor(audioObj.currentTime / 60); // 分数
+ sec = sec.substr(sec.length-2, 2);
+ min = min.substr(min.length-2, 2);
+ var totalSec = '0' + Math.floor(audioObj.duration % 60); // 秒数
+ var totalMin = '0' + Math.floor(audioObj.duration / 60); // 分数
+ totalSec = totalSec.substr(totalSec.length-2, 2);
+ totalMin = totalMin.substr(totalMin.length-2, 2);
+ crtTime.innerHTML = min+":"+sec+' ['+ totalMin+':'+totalSec +']';
+ }, true);
+
+ document.getElementById('name').innerHTML=name;
+ pButton.innerHTML = 'START';
+}
+// イベント設定
+var crtTime = document.getElementById('ctime');
+var pButton = document.getElementById('playButton');
+// 再生ボタンのイベントを設定
+pButton.addEventListener('click', function(){
+ if (playFlag == false){
+ playFlag = true; // 再生フラグ:再生中にする
+ audioObj.play();
+ pButton.innerHTML = 'STOP';
+ } else if (playFlag == true){
+ playFlag = false; // 再生フラグ:停止中にする
+ audioObj.pause();
+ pButton.innerHTML = 'PLAY';
+ } else {
+ alert("曲をセットしてね!");
+ }
+}, true);
diff --git a/public/music/Ray_at_midsummer.mp3 b/public/music/Ray_at_midsummer.mp3
new file mode 100644
index 0000000..68bdffa
--- /dev/null
+++ b/public/music/Ray_at_midsummer.mp3
Binary files differ
diff --git a/public/music/cat_life.mp3 b/public/music/cat_life.mp3
new file mode 100644
index 0000000..a581144
--- /dev/null
+++ b/public/music/cat_life.mp3
Binary files differ
diff --git a/public/music/hayaoshi.mp3 b/public/music/hayaoshi.mp3
new file mode 100755
index 0000000..552eebf
--- /dev/null
+++ b/public/music/hayaoshi.mp3
Binary files differ
diff --git a/views/controller.erb b/views/controller.erb
new file mode 100644
index 0000000..afa6b98
--- /dev/null
+++ b/views/controller.erb
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ イントロクイズ2019
+
+
+
+
+
+
+
+
+
+ <%=@pushed%>
+
+
+
diff --git a/views/error.erb b/views/error.erb
new file mode 100644
index 0000000..9dd6764
--- /dev/null
+++ b/views/error.erb
@@ -0,0 +1,13 @@
+
+
+
+
+
+ イントロクイズ2019
+
+
+ エラーが発生しました
+ もう一度お試しください
+ 最初のページに戻る
+
+
diff --git a/views/index.erb b/views/index.erb
new file mode 100644
index 0000000..920be33
--- /dev/null
+++ b/views/index.erb
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+ イントロクイズ2019
+
+
+
+ チームを選択し直す
+
+ <%=@pushed%>
+
+
+
diff --git a/views/select.erb b/views/select.erb
new file mode 100644
index 0000000..9c77fb4
--- /dev/null
+++ b/views/select.erb
@@ -0,0 +1,22 @@
+
+
+
+
+
+ イントロクイズ2019
+
+
+ チーム選択
+
+
+