diff --git a/plugins/qlab/cue.ejs b/plugins/qlab/cue.ejs
new file mode 100644
index 0000000..3ebc644
--- /dev/null
+++ b/plugins/qlab/cue.ejs
@@ -0,0 +1,134 @@
+
+
+ |
+
+
+ <% if(cue.broken){ %>
+
+
+ <% }else if(cue.running){ %>
+
+
+ <% }else if(cue.loaded){ %>
+
+
+ <% }else if(cue.flagged){ %>
+
+
+ <% }else{ %>
+
+ <% } %>
+ |
+
+
+ .replace(' ','-') %>.png) |
+ <%= cue.number || " " %> |
+
+
+
+
+ <% for(let i=cue.nestedGroupModes.length; i>0; i--){ %>
+
+ <% let test = "" %>
+
+ <% if(cue.type=="Group" && i==1 && cue.cues.length==0){ %>
+ <%= test %> |
+
+ <% }else if(cue.type=="Group" && i==1){ %>
+ <%= test %> |
+
+ <% }else if(cue.nestedGroupPosition[i-1]==0 && cue.nestedGroupPosition[i-2]==0){ %>
+ <%= test %> |
+
+ <% }else if(i==1 && cue.nestedGroupPosition[i]==0){ %>
+ <%= test %> |
+
+ <% }else{ %>
+ <%= test %> |
+
+ <% } %>
+
+ <% } %>
+
+
+
+
+
+
+ <% if(cue.type=="Group"){ %>
+ <% if(cue.cues.length==0){ %>
+ <%= cue.listName %> |
+
+ <% }else{ %>
+ <%= cue.listName %> |
+
+ <% } %>
+
+ <% }else if(cue.nestedGroupPosition[1]==0){ %>
+ <%= cue.listName %> |
+
+ <% }else{ %>
+ <%= cue.listName %> |
+
+ <% } %>
+
+
+ <%= cue.target %> |
+
+
+
+
+ <% if(cue.preWait){ %>
+ <%= prettyFormatTime(cue.preWait) %> |
+
+ <% }else{ %>
+ 00:00.00 |
+
+ <% } %>
+
+
+
+ <% if(cue.duration || (cue.type=="Group" && cue.mode==3)){ %>
+ <%= prettyFormatTime(cue.duration) %> |
+
+ <% }else{ %>
+ |
+
+ <% } %>
+
+
+
+ <% if(cue.postWait){ %>
+ <%= prettyFormatTime(cue.postWait) %> |
+
+ <% }else{ %>
+ 00:00.00 |
+
+ <% } %>
+
+
+
+ <% if(cue.continueMode==2){ %>
+  |
+
+ <% }else if(cue.continueMode==1){ %>
+  |
+
+ <% }else{ %>
+ |
+
+ <% } %>
+
+
+
+
+<%
+ function prettyFormatTime(seconds){
+ var startIndex = 14;
+ if(seconds>=3600){
+ startIndex = 11;
+ }
+ var string = new Date(seconds * 1000).toISOString()
+ return string.substring(startIndex, string.length-2)
+ }
+%>
\ No newline at end of file
diff --git a/plugins/qlab/cuelist.ejs b/plugins/qlab/cuelist.ejs
new file mode 100644
index 0000000..0887cbe
--- /dev/null
+++ b/plugins/qlab/cuelist.ejs
@@ -0,0 +1,54 @@
+
+
+
+
+ |
+ |
+ |
+ Number |
+ |
+ Q |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ Target |
+ Pre Wait |
+ Action |
+ Post Wait |
+
+
+ |
+
+
+
+ <% for(const cue_id in cueList.cues){ %>
+ <%= displayCueRow(cueList.cues[cue_id]) %>
+ <% } %>
+
+
+
+
+
+<%
+
+
+
+function displayCueRow(q){
+ let html = "";
+
+ html+= rowTemplate({cue: allCues[q.uniqueID], allCues: allCues});
+
+ if(q.cues){
+ q.cues.forEach(q =>{
+ html+=displayCueRow(q);
+ })
+ }
+ return html;
+}
+%>
\ No newline at end of file
diff --git a/plugins/qlab/qlab.css b/plugins/qlab/qlab.css
index 155ee66..ef8da5f 100644
--- a/plugins/qlab/qlab.css
+++ b/plugins/qlab/qlab.css
@@ -219,10 +219,19 @@ tr.playback-position .q-gray-text {
border-color: #a601c0;
background-color: #592d74;
}
+.cartColor-none{
+ border-color: #95929f;
+ background-color: #3b3b3b
+}
.cartBlank{
border-color: #1f1f1f;
background-color: #1f1f1f;
}
+.cartCueWrapper.playback-position .cartCue{
+ border-color: #88b3db;
+ outline: #88b3db 1px solid;
+ box-shadow: 0px 0px 3px 3px #88b3db, inset 0px 0px 5px #88b3db;
+}
@media screen and (min-width: 0px) and (max-width: 750px) {
diff --git a/plugins/qlab/qlab.html b/plugins/qlab/qlab.html
index ba75dde..fb590fc 100644
--- a/plugins/qlab/qlab.html
+++ b/plugins/qlab/qlab.html
@@ -1,269 +1,101 @@
-<% function cueTemplate(cueList, groupModes, depth, workspace){ %>
-
- <% depth++ %>
-
- <% for(var c in cueList.cues){ %>
-
- <% var cue = cueList.cues[c] %>
-
-
- |
-
-
- <% if(cue.broken){ %>
-
- <% }else if(cue.running){ %>
-
- <% }else if(cue.loaded){ %>
-
- <% }else if(cue.flagged){ %>
-
- <% }else{ %>
-
- <% } %>
- |
-
-
-  %>.png) |
- <%- cue.number || " " %> |
-
- <%
- var nextCueDepth = 0;
- if(workspace.allCuesOrdered[cue.index+1]!=undefined){
- nextCueDepth = workspace.allCuesOrdered[cue.index+1].groups.length-1;
- }
-
- %>
-
-
-
-
- <% for(var i=0; i
-
- <% if(i==depth-1 && cue.type=="Group"){ %>
-
- <% if(cue.cues.length==0){ %>
- |
-
- <% }else{ %>
- |
-
- <% } %>
-
- <% }else if(i
- |
-
- <% }else if(i>=nextCueDepth && i
- |
-
- <% }else if(i==depth-1 && i>nextCueDepth){ %>
- |
-
- <% }else{ %>
- |
-
- <% } %>
-
- <% } %>
-
-
-
-
- <% if(cue.type=="Group"){ %>
- <% if(cue.cues.length==0){ %>
- <%= cue.listName %> |
-
- <% }else{ %>
- <%= cue.listName %> |
-
- <% } %>
- <% }else if(depth>nextCueDepth+1){ %>
- <%= cue.listName %> |
-
- <% }else if(depth>1){ %>
- <%= cue.listName %> |
-
- <% }else{ %>
- <%= cue.listName %> |
-
- <% } %>
-
- <%= cue.target %> |
-
- <% if(cue.preWait){ %>
- <%= prettyFormatTime(cue.preWait) %> |
- <% }else{ %>
- 00:00.00 |
- <% } %>
-
- <% if(cue.duration || (cue.type=="Group" && cue.mode==3)){ %>
- <% %>
- <%= prettyFormatTime(cue.duration) %> |
- <% }else{ %>
- |
- <% } %>
-
- <% if(cue.postWait){ %>
- <%= prettyFormatTime(cue.postWait) %> |
- <% }else{ %>
- 00:00.00 |
- <% } %>
-
- <% if(cue.continueMode==2){ %>
-  |
- <% }else if(cue.continueMode==1){ %>
-  |
- <% }else{ %>
- |
- <% } %>
-
-
-
-
-
-
-
-
-
- <% if(cue.cues){ %>
- <% groupModes.push(cue.mode) %>
- <% cueTemplate(cue, groupModes, depth, workspace) %>
- <% } %>
-
- <% } %>
- <% groupModes.pop(); %>
-<% } %>
-
-<% function cartTemplate(cueList, groupModes, depth, workspace){ %>
-
-<% } %>
-
- <%= listName %>
- <%= data.version || "" %>
+ <%= listName %>
+ <%= data.version || "" %>
+<%
+ const fs = require('fs');
+ let _ = require('lodash');
-<% for(var i in data.workspaces){ %>
- <% for(var j in data.workspaces[i].cueLists){ %>
- <%
- let cueList = data.workspaces[i].cueLists[j];
- let width = 100/cueList.cartRows;
- let height = 600/(cueList.cartColumns+1);
- %>
+ let cueTemplate = _.template(fs.readFileSync(`./plugins/qlab/cue.ejs`));
+ let tileTemplate = _.template(fs.readFileSync(`./plugins/qlab/tile.ejs`));
+ let cartTemplate = _.template(fs.readFileSync(`./plugins/qlab/cart.ejs`));
+ let listTemplate = _.template(fs.readFileSync(`./plugins/qlab/cuelist.ejs`));
- <%= cueList.listName %> - <%= data.workspaces[i].displayName %>
+%>
-
-
- <% if(cueList.type=="Cart"){ %>
-
-
-
- <% for(let r=0; r
- <% for(let c=0; c
-
- <% } %>
- <% } %>
-
-
- <% for(var c in cueList.cues){
- let cue = cueList.cues[c];
-
- if(!cue.cartPosition){
- continue;
- }
-
- let row = cue.cartPosition[0]-1;
- let col = cue.cartPosition[1]-1;
-
- let colPos = width*col;
- let rowPos = height*row;
-
- if(col>=cueList.cartColumns || row>=cueList.cartRows){
- continue;
- }
- %>
-
-
-
-
- <% if(cue.broken){ %>
-

- <% }else if(cue.running){ %>
-

- <% }else{ %>
-

- <% } %>
-
-
-
<%= cue.number %> • <%= cue.displayName %>
-
-
-
-
- <% } %>
-
-
-
-
-
- <% }else{ %>
-
- <% cueListOffset = 0 %>
-
-
- |
- |
- |
- Number |
- |
- Q |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- Target |
- Pre Wait |
- Action |
- Post Wait |
-
-
- |
-
- <% cueTemplate(cueList, [], [], data.workspaces[i]); %>
-
-
- <% } %>
- <% } %>
-
-<% } %>
<%
- function prettyFormatTime(seconds){
- var startIndex = 14;
- if(seconds>=3600){
- startIndex = 11;
- }
- var string = new Date(seconds * 1000).toISOString()
- return string.substring(startIndex, string.length-2)
- }
+ for(const workspace_id in data.workspaces){
+ const workspace = data.workspaces[workspace_id];
+
+ for(const cueList_id in workspace.cueLists){
+ const ql = workspace.cueLists[cueList_id];
+%>
+
+
+
+ <% if(ql.type=="Cue List"){ %>
+
+ <%= listTemplate({cueList: ql, allCues: workspace.cues, rowTemplate: cueTemplate}) %>
+
+ <% }else if(ql.type=="Cart"){ %>
+
+ <%= cartTemplate({cueList: ql, allCues: workspace.cues, tileTemplate: tileTemplate}) %>
+
+
+ <% } %>
+
+
+
+<% }} %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-%>
\ No newline at end of file
diff --git a/plugins/qlab/qlab.js b/plugins/qlab/qlab.js
index 3403cd7..3bfcad4 100644
--- a/plugins/qlab/qlab.js
+++ b/plugins/qlab/qlab.js
@@ -1,3 +1,5 @@
+let _ = require('lodash');
+
exports.defaultName = 'QLab 4';
exports.connectionType = 'osc';
exports.searchOptions = {
@@ -9,153 +11,230 @@ exports.ready = function (device) {
device.send('/workspaces');
};
-exports.data = function (device, osc) {
- const address = osc.address;
- const p = osc.address.split('/');
- p.shift();
+exports.data = function (device, oscData) {
+ const address = oscData.address;
+ let osc = oscData.address.split('/');
+ osc.shift();
+ let data;
+
+
//console.log(address)
- if (osc.address == '/reply/workspaces') {
- const d = JSON.parse(osc.args[0]).data;
- device.data.workspaces = {};
- for (let i = 0; i < d.length; i++) {
- device.data.workspaces[d[i].uniqueID] = {
- displayName: d[i].displayName,
- selectionIsPlayhead: true,
- };
- device.data.version = d[i].version;
- device.send(`/workspace/${d[i].uniqueID}/cueLists`);
- device.send(`/workspace/${d[i].uniqueID}/updates`, [
- { type: 'i', value: 1 },
- ]);
- device.send(`/cue/playbackPosition/uniqueID`);
- device.send(`/workspace/${d[i].uniqueID}/selectionIsPlayhead`);
- }
- } else if (p[1] == 'workspace' && p[3] == 'cueLists') {
- const workspace = p[2];
- const d = JSON.parse(osc.args[0]).data;
- device.data.workspaces[workspace].cueLists = {};
- device.data.workspaces[workspace].allCues = {};
- device.data.workspaces[workspace].allCuesOrdered = [];
- device.data.lastCueInGroup = {};
+ if(match(osc, ["reply", "version"])){ // heartbeat response
- for (let i in d) {
- device.data.workspaces[workspace].cueLists[d[i].uniqueID] = d[i];
- device.data.workspaces[workspace].cueLists[d[i].uniqueID].cues =
- d[i].cues;
+ this.deviceInfoUpdate(device, 'status', 'ok');
- device.data.workspaces[workspace].allCues[d[i].uniqueID] = d[i];
- if(d[i].type=="Cart"){
- device.send(`/cue_id/${d[i].uniqueID}/cartColumns`);
- device.send(`/cue_id/${d[i].uniqueID}/cartRows`);
+
+ }else if(match(osc, ["reply", "workspaces"])){
+
+ let json = JSON.parse(oscData.args[0]);
+ json.data.forEach(wksp => {
+ device.data.workspaces = {};
+
+ device.data.workspaces[wksp.uniqueID] = {
+ uniqueID: wksp.uniqueID,
+ displayName: wksp.displayName,
+ cueLists: {},
+ cues: {}
}
- function getMoreCueInfo(group, groups_arg) {
- const groups = JSON.parse(JSON.stringify(groups_arg));
- groups.push(group.uniqueID);
+ device.send(`/workspace/${wksp.uniqueID}/connect`);
- for (let cueIndex in group.cues) {
- const cue = group.cues[cueIndex];
- //console.log(cue)
-
- device.data.lastCueInGroup[group.uniqueID] = cue;
-
- cue.groups = groups;
- device.send(
- `/workspace/${workspace}/cue_id/${cue.uniqueID}/valuesForKeys`,
- [
- {
- type: 's',
- value:
- '["mode", "parent", "isBroken", "preWait", "duration", "postWait", "continueMode", "cueTargetNumber", "isLoaded", "isRunning", "cartPosition", "displayName"]',
- },
- ]
- );
- cue.index =
- device.data.workspaces[workspace].allCuesOrdered.push(cue) - 1;
-
- if (cue.cues) {
- cue.mode = 0;
- getMoreCueInfo(cue, groups);
- }
-
- device.data.workspaces[workspace].allCues[cue.uniqueID] = cue;
-
-
- }
- }
-
- getMoreCueInfo(d[i], []);
+ });
+ }else if(match(osc, ["reply", "workspace", "*", "connect"])){
+
+ device.send(`/workspace/${osc[2]}/updates`, [
+ { type: 'i', value: 1 },
+ ]);
+
+ device.send(`/workspace/${osc[2]}/cueLists`);
+
+
+
+ }else if(match(osc, ["reply", "workspace", "*", "cueLists"])){
+
+ this.deviceInfoUpdate(device, 'status', 'ok');
+
+ let workspace = device.data.workspaces[osc[2]];
+ let json = JSON.parse(oscData.args[0]);
+
+ workspace.cueLists = {};
+ workspace.cues = {};
+
+
+ json.data.forEach(ql => {
+ workspace.cueLists[ql.uniqueID] = ql;
+ addChildrenToWorkspace(workspace, ql);
+ });
+
+ device.draw();
+
+ setTimeout(function(){
+ json.data.forEach(ql => {
+ workspace.cueLists[ql.uniqueID] = ql;
+ getValuesForKeys(device, json.workspace_id, ql);
+ });
+ }, 0);
+
+
+
+ }else if(match(osc, ["reply", "cue_id", "*", "children"])){
+
+ let json = JSON.parse(oscData.args[0]);
+ let workspace = device.data.workspaces[json.workspace_id];
+ let cue_id = osc[2];
+
+ workspace.cueLists[cue_id].cues = json.data;
+ addChildrenToWorkspace(workspace, workspace.cueLists[cue_id]);
+
+ device.draw();
+
+ getValuesForKeys(device, json.workspace_id, workspace.cues[cue_id]);
+
+
+
+ }else if(match(osc, ["reply", "cue_id", "*", "valuesForKeys"])){
+
+ let json = JSON.parse(oscData.args[0]);
+ let cueValues = json.data;
+
+ let workspace = device.data.workspaces[json.workspace_id];
+ let cue = workspace.cues[cueValues.uniqueID];
+
+ if(!cue){
+ cue = workspace.cues[cueValues.uniqueID] = {};
}
- // console.log(device.data.lastCueInGroup)
+ cue.uniqueID = cueValues.uniqueID;
+ cue.number = cueValues.number;
+ cue.name = cueValues.name;
+ cue.listName = cueValues.listName;
+ cue.displayName = cueValues.displayName;
+ cue.broken = cueValues.isBroken;
+ cue.running = cueValues.isRunning;
+ cue.loaded = cueValues.isLoaded;
+ cue.flagged = cueValues.isFlagged;
+ cue.type = cueValues.type;
+ cue.cues = cueValues.children;
+ cue.preWait = cueValues.preWait;
+ cue.postWait = cueValues.postWait;
+ cue.duration = cueValues.currentDuration;
+ cue.colorName = cueValues.colorName;
+ cue.continueMode = cueValues.continueMode;
+ cue.groupMode = cueValues.mode;
+ cue.parent = cueValues.parent;
+ cue.cartRows = cueValues.cartRows;
+ cue.cartColumns = cueValues.cartColumns;
+ cue.cartPosition = cueValues.cartPosition;
- device.draw();
- } else if (p[0] == 'update' && p[1] == 'workspace' && p[3] == 'cue_id') {
- device.send(`/workspace/${p[2]}/cueLists`);
- } else if (
- p[0] == 'update' &&
- p[1] == 'workspace' &&
- p[5] == 'playbackPosition'
- ) {
- //console.log('pb move');
- device.data.playbackPosition = osc.args[0];
- device.draw();
- } else if (p[0] == 'reply' && p[1] == 'cue_id') {
+ let nestedGroupModes = [];
+ let nestedGroupPosition = [0];
+ var obj = cue;
- const d = JSON.parse(osc.args[0]);
- const cueID = p[2];
- const workspace = d.workspace_id;
- const cueList = d.data.parent;
+ while(obj.parent!="[root group of cue lists]"){
+ nestedGroupModes.push(obj.groupMode);
- if (p[3] == 'uniqueID') {
- // response to /cue/playbackPosition/uniqueID
- device.data.playbackPosition = d.data;
+ let pos = _.findIndex(workspace.cues[obj.parent].cues, {uniqueID: obj.uniqueID});
+ pos= Math.abs(pos-workspace.cues[obj.parent].cues.length)-1;
+ nestedGroupPosition.push(pos)
- } else if (p[3] == 'mode') {
- device.data.workspaces[workspace].allCues[cueID].mode = d.data;
-
- } else if (p[3] == 'valuesForKeys') {
- this.deviceInfoUpdate(device, 'status', 'ok');
-
- device.data.workspaces[workspace].allCues[cueID].mode = d.data.mode;
- device.data.workspaces[workspace].allCues[cueID].preWait = d.data.preWait;
- device.data.workspaces[workspace].allCues[cueID].duration =
- d.data.duration;
- device.data.workspaces[workspace].allCues[cueID].postWait =
- d.data.postWait;
- device.data.workspaces[workspace].allCues[cueID].continueMode =
- d.data.continueMode;
- device.data.workspaces[workspace].allCues[cueID].target =
- d.data.cueTargetNumber;
- device.data.workspaces[workspace].allCues[cueID].loaded = d.data.isLoaded;
- device.data.workspaces[workspace].allCues[cueID].broken = d.data.isBroken;
- device.data.workspaces[workspace].allCues[cueID].running =
- d.data.isRunning;
- device.data.workspaces[workspace].allCues[cueID].cartPosition =
- d.data.cartPosition;
- device.data.workspaces[workspace].allCues[cueID].displayName =
- d.data.displayName;
- }else if(p[3] == 'cartColumns'){
- device.data.workspaces[workspace].allCues[cueID].cartColumns = d.data;
- }else if(p[3] == 'cartRows'){
- device.data.workspaces[workspace].allCues[cueID].cartRows = d.data;
+ obj = workspace.cues[obj.parent];
}
- device.draw();
- } else if (p[1] == 'workspace' && p[3] == 'selectionIsPlayhead') {
- device.data.workspaces[p[2]].selectionIsPlayhead = JSON.parse(
- osc.args[0]
- ).data;
- } else {
+ cue.nestedGroupModes = nestedGroupModes;
+ cue.nestedGroupPosition = nestedGroupPosition;
+
+ device.update("updateCueData", {cue: cue, allCues: workspace.cues, workspace: workspace});
+
+
+
+ }else if(match(osc, ["update", "workspace", "*", "cue_id", "*"])){
+
+ let workspace = device.data.workspaces[osc[2]];
+ let cueLists = Object.keys(workspace.cueLists);
+ let cue_id = osc[4];
+
+ if(cueLists.includes(cue_id)){
+ device.send(`/workspace/${workspace.uniqueID}/cue_id/${cue_id}/children`);
+ }
+
+ device.send(`/workspace/${osc[2]}/cue_id/${cue_id}/valuesForKeys`, [
+ {type: 's', value: valuesForKeysString}
+ ]);
+
+
+ }else if(match(osc, ["update", "workspace", "*"])){
+ // nothing here yet
+ // occurs when cue lists are reordered or a list is deleted
+ device.send(`/workspace/${osc[2]}/cueLists`);
+
+
+ }else if(match(osc, ["update", "workspace", "*", "dashboard"])){
+ // nothing here yet
+
+
+ }else if(match(osc, ["update", "workspace", "*", "cueList", "*", "playbackPosition"])){
+
+ let workspace = device.data.workspaces[osc[2]];
+ let cue = workspace.cues[oscData.args[0]];
+
+ if(cue) device.update("updatePlaybackPosition", {cue: cue});
+
+ }else{
+ console.log(address)
}
+
+
+
};
+const valuesForKeysString =
+'["uniqueID","number","name","listName","isBroken","isRunning","isLoaded","isFlagged",'
++'"type","children","preWait","postWait","currentDuration","colorName","continueMode",'
++'"mode","parent","cartRows","cartColumns","cartPosition","displayName"]';
+
+function addChildrenToWorkspace(workspace, q){
+ workspace.cues[q.uniqueID] = q;
+ workspace.cues[q.uniqueID].nestedGroupModes = [];
+ workspace.cues[q.uniqueID].nestedGroupPosition = [];
+
+ if(q.cues){
+ q.cues.forEach(q => {
+ addChildrenToWorkspace(workspace, q);
+ });
+ }
+}
+
+
+function getValuesForKeys(device, workspace_id, q){
+ device.send(`/workspace/${workspace_id}/cue_id/${q.uniqueID}/valuesForKeys`, [
+ {type: 's', value: valuesForKeysString}
+ ]);
+ if(q.cues){
+ q.cues.forEach(q => {
+ getValuesForKeys(device, workspace_id, q);
+ });
+ }
+}
+
+
+function match(osc, array){
+ let out = true;
+ if(osc.length!=array.length){
+ return false;
+ }
+ array.forEach((match, i)=> {
+ if(osc[i]!=match && match!="*"){
+ out = false;
+ }
+ });
+ return out;
+}
+
+
exports.heartbeat = function (device) {
- try {
- device.send(`/workspace/${Object.keys(device.data.workspaces)[0]}/thump`);
- } catch (err) {}
-};
+ device.send(`/version`);
+};
\ No newline at end of file
diff --git a/plugins/qlab/tile.ejs b/plugins/qlab/tile.ejs
new file mode 100644
index 0000000..7d8a128
--- /dev/null
+++ b/plugins/qlab/tile.ejs
@@ -0,0 +1,39 @@
+<%
+
+ let height = 0;
+ let width = 0;
+ let left = 0;
+ let top = 0;
+
+
+ if(cue.parent){
+ width = 100/allCues[cue.parent].cartColumns;
+ height = 600/allCues[cue.parent].cartRows;
+
+ top = (cue.cartPosition[0]-1)*height;
+ left = (cue.cartPosition[1]-1)*width;
+ }
+
+%>
+
+
+
+
+ <% if(cue.broken){ %>
+

+ <% }else if(cue.running){ %>
+

+ <% }else{ %>
+

+ <% } %>
+
+
+
<%= cue.number %> • <%= cue.displayName %>
+
+
+
\ No newline at end of file