Commit 84829139 authored by Jens-Christian Fischer's avatar Jens-Christian Fischer
Browse files

Refactor JS for better modularity

- Break out player and conductor view
- separate js libraries (doesn't quite work yet)
- different layouts for player / conductor
parent 2020a336
......@@ -2,7 +2,16 @@ exports.config = {
// See http://brunch.io/#documentation for docs.
files: {
javascripts: {
joinTo: "js/app.js"
joinTo: {
'js/app.js' : [/^(?!node_modules\/p5)/,
/^(?!node_modules\/osc)/,
/(?!presencesketch.js$)/
],
'js/myp5.js' : [/^(node_modules\/p5)/,
/^(node_modules\/osc)/,
/presencesketch.js$/
]
}
// To use a separate vendor.js bundle, specify two files path
// http://brunch.io/docs/config#-files-
......
......@@ -3,7 +3,7 @@ defmodule Grains.Mixfile do
def project do
[app: :grains,
version: "0.0.59",
version: "0.0.60",
elixir: "~> 1.2",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix, :gettext] ++ Mix.compilers,
......
defmodule Grains.GrainsController do
use Grains.Web, :controller
def index(conn, _) do
render conn, "index.html"
end
def show(conn, %{"grain" => grain}) do
render conn, "#{grain}.html", grain: grain
end
......
......@@ -14,14 +14,27 @@ defmodule Grains.Router do
plug :accepts, ["json"]
end
pipeline :conductor_layout do
plug :put_layout, {Grains.LayoutView, :conductor}
end
scope "/", Grains do
pipe_through :browser # Use the default browser stack
get "/play", GrainsController, :index
get "/grains/:grain", GrainsController, :show
get "/", PageController, :index
get "/conductor", ConductorController, :index
end
scope "/conductor", Grains do
pipe_through [:browser, :conductor_layout]
get "/", ConductorController, :index
end
# Other scopes may use custom stacks.
# scope "/api", Grains do
# pipe_through :api
......
......@@ -9,3 +9,9 @@
display: block;
height: 40px;
}
.play {
text-align: center;
font-weight: bold;
font-size: 2em;
}
......@@ -15,112 +15,30 @@ import "phoenix_html";
import {Socket, Presence} from "phoenix"
import _ from "underscore"
import socket from "./socket"
import SliderPanel from "./slider";
import OrientationPanel from "./orientation";
import AccelerationPanel from "./acceleration";
import BpmPanel from "./bpm.js";
import loadView from "./views/loader.js";
import PresenceSketch from "./presencesketch";
import _ from "underscore";
import socket from "./socket";
SliderPanel.init(socket, "sliderPanel", ["s1", "s2", "s3"]);
OrientationPanel.init(socket, "orientationPanel");
AccelerationPanel.init(socket, "accelerationPanel");
BpmPanel.init(socket, "bpmPanel");
PresenceSketch.init(socket, "sketch");
function handleDOMContentLoaded() {
// Get the current view name
const viewName = document.getElementsByTagName('body')[0].dataset.jsViewName;
// Load view class and mount it
const ViewClass = loadView(viewName);
const view = new ViewClass();
view.mount();
// Presence
let presences = {}
let formatTimestamp = (timestamp) => {
let date = new Date(timestamp)
return date.toLocaleDateString()
window.currentView = view;
}
let listBy = (user, {metas: metas}) => {
return {
user: user,
onlineAt: formatTimestamp(metas[0].online_at)
};
function handleDocumentUnload() {
window.currentView.unmount();
}
let userList = document.getElementById("UserList");
let userCounter = document.getElementById("UserCount");
let render = (presences, diff) => {
if (diff) {
PresenceSketch.addBoids(diff.joins);
PresenceSketch.removeBoids(diff.leaves);
} else {
PresenceSketch.syncBoids(presences);
}
if (userList) {
userList.innerHTML = Presence.list(presences, listBy)
.map(presence => `
<li>
<b>${presence.user}</b>
<br><small>online since ${presence.onlineAt}</small>
</li>
`)
.join("");
}
if (userCounter) {
userCounter.innerHTML = Presence.list(presences).length;
}
}
let room = socket.channel("presence:lobby", {})
room.on("presence_state", state => {
console.log("p_s");
presences = Presence.syncState(presences, state)
render(presences)
})
room.on("presence_diff", diff => {
console.log("p_d");
console.log(diff);
presences = Presence.syncDiff(presences, diff);
render(presences, diff)
})
console.log("joining presence");
room.join().receive("ok", resp => {
console.log("presence joined");
})
.receive("error", reason => console.log("presence join failed", reason))
if (document.getElementById('sketch')) {
let visuals = socket.channel("visuals:data", {});
visuals.on("velocity", state => {
console.log("velocity: ", state);
PresenceSketch.set_velocity(state.user, state.dy, state.dx);
});
visuals.on("acceleration", state => {
console.log("acceleration: ", state);
PresenceSketch.set_acceleration(state.user, state.force);
});
visuals.join().receive("ok", resp => {
console.log("visual joined");
})
.receive("error", reason => console.log("visual join failed", reason))
}
window.addEventListener('DOMContentLoaded', handleDOMContentLoaded, false);
window.addEventListener('unload', handleDocumentUnload, false);
// Import local files
......
......@@ -256,7 +256,7 @@ Boid.prototype.render_star = function() {
function Master(p, x, y, type) {
Boid.call(this, p, x, y, null);
this.radius = 32;
this.radius = 48;
this.type = type;
this.cohesion_f = 6;
this.separation_f = 1;
......
import MainView from "./main";
import {Socket, Presence} from "phoenix";
import socket from "../socket";
import BpmPanel from "../bpm.js";
import PresenceSketch from "../presencesketch";
export default class View extends MainView {
mount() {
super.mount();
BpmPanel.init(socket, "bpmPanel");
PresenceSketch.init(socket, "sketch");
if (document.getElementById('sketch')) {
let visuals = socket.channel("visuals:data", {});
visuals.on("velocity", state => {
console.log("velocity: ", state);
PresenceSketch.set_velocity(state.user, state.dy, state.dx);
});
visuals.on("acceleration", state => {
console.log("acceleration: ", state);
PresenceSketch.set_acceleration(state.user, state.force);
});
visuals.join().receive("ok", resp => {
console.log("visual joined");
})
.receive("error", reason => console.log("visual join failed", reason));
}
console.log("ConductorIndexView mounted");
}
unmount() {
super.unmount();
console.log("ConductorIndexView unmounted");
}
render(presences, diff) {
console.log("ConductorIndexView.render()")
super.render(presences, diff);
if (diff) {
PresenceSketch.addBoids(diff.joins);
PresenceSketch.removeBoids(diff.leaves);
} else {
PresenceSketch.syncBoids(this._presences);
}
}
}
import MainView from './main';
import ConductorIndexView from './conductor';
import GrainsIndexView from './player';
// Collection of specific view modules
const views = {
ConductorIndexView,
GrainsIndexView
};
export default function loadView(viewName) {
return views[viewName] || MainView;
}
import {Socket, Presence} from "phoenix"
import socket from "../socket";
let formatTimestamp = (timestamp) => {
let date = new Date(timestamp);
return date.toLocaleDateString();
}
let listBy = (user, {metas: metas}) => {
return {
user: user,
onlineAt: formatTimestamp(metas[0].online_at)
};
}
export default class MainView {
constructor() {
this._presences = {};
}
mount() {
// This will be executed when the document loads..
let room = socket.channel("presence:lobby", {});
room.on("presence_state", state => {
console.log("p_s");
this._presences = Presence.syncState(this._presences, state)
this.render(this._presences)
});
room.on("presence_diff", diff => {
console.log("p_d");
console.log(diff);
this._presences = Presence.syncDiff(this._presences, diff);
this.render(this._presences, diff)
});
console.log("joining presence");
room.join().receive("ok", resp => {
console.log("presence joined");
})
.receive("error", reason => console.log("presence join failed", reason));
console.log('MainView mounted');
}
unmount() {
// This will be executed when the document unloads...
console.log('MainView unmounted');
}
get presences() { return this._presences; }
set presences(p) {this._presences = p; }
render(presences, diff) {
console.log("MainView.render()");
let userList = document.getElementById("UserList");
let userCounter = document.getElementById("UserCount");
if (userList) {
userList.innerHTML = Presence.list(presences, listBy)
.map(presence => `
<li>
<b>${presence.user}</b>
<br><small>online since ${presence.onlineAt}</small>
</li>
`)
.join("");
}
if (userCounter) {
userCounter.innerHTML = Presence.list(presences).length;
}
}
}
MainView._presences = {};
import MainView from "./main";
import socket from "../socket";
import SliderPanel from "../slider";
import OrientationPanel from "../orientation";
import AccelerationPanel from "../acceleration";
export default class View extends MainView {
mount() {
super.mount();
// SliderPanel.init(socket, "sliderPanel", ["s1", "s2", "s3"]);
OrientationPanel.init(socket, "orientationPanel");
AccelerationPanel.init(socket, "accelerationPanel");
console.log("PlayerView mounted");
}
unmount() {
super.unmount();
console.log("PlayerView unmounted");
}
}
<h1>Play</h1>
<p>Orientation</p>
<div id="orientationPanel" class="interfacePanel"></div>
Acceleration
<div id="accelerationPanel" class="interfacePanel"></div>
......@@ -11,7 +11,7 @@
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>
<body>
<body data-js-view-name="<%= js_view_name(@conn, @view_template) %>">
<div class="container">
<header class="header">
<img src="/images/switch.png" alt="SWITCH" height="46" width="180" />
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Conductor</title>
<link rel="stylesheet" href="<%= static_path(@conn, "/css/app.css") %>">
</head>
<body data-js-view-name="<%= js_view_name(@conn, @view_template) %>">
<div class="container">
<header class="header">
<img src="/images/switch.png" alt="SWITCH" height="46" width="180" />
<p>Online players: <span id="UserCount"></span></p>
</header>
<main role="main">
<%= render @view_module, @view_template, assigns %>
</main>
</div> <!-- /container -->
<script>window.userToken = "<%= assigns[:user_token] %>"</script>
<script src="<%= static_path(@conn, "/js/myp5.js") %>"></script>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
</body>
</html>
......@@ -5,15 +5,5 @@
</div>
<div>
Choose your sound:
<ul>
<li>
<a href="/grains/rhythm">Rhythm</a>
</li>
<li><a href="/grains/bass">Bass</a></li>
<li><a href="/grains/chords">Chords</a></li>
<li><a href="/grains/melody">Melody</a></li>
</li>
</ul>
<a href="/play" class="btn-lg btn-success btn-block play">Play</a>
</div>
defmodule Grains.LayoutView do
use Grains.Web, :view
@doc """
Generates name for the JavaScript view we want to use
in this combination of view/template.
"""
def js_view_name(conn, view_template) do
[view_name(conn), template_name(view_template)]
|> Enum.reverse
|> List.insert_at(0, "view")
|> Enum.map(&String.capitalize/1)
|> Enum.reverse
|> Enum.join("")
end
# Takes the resource name of the view module and removes the
# the ending *_view* string.
defp view_name(conn) do
conn
|> view_module
|> Phoenix.Naming.resource_name
|> String.replace("_view", "")
end
# Removes the extion from the template and reutrns
# just the name.
defp template_name(template) when is_binary(template) do
template
|> String.split(".")
|> Enum.at(0)
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment