window.twirl = {
debug: false, //window.location.href.startsWith("file://"),
themes: ["Default", "Basic", "Hacker", "Monoclassic", "Doze"],
audioTypes: ["audio/mpeg", "audio/mp4", "audio/ogg", "audio/vorbis", "audio/x-flac","audio/aiff","audio/x-aiff", "audio/vnd.wav", "audio/wave", "audio/x-wav", "audio/wav", "audio/flac"],
maxFileSize: 1e+8, // 100MB
latencyCorrection: 40,
storage: {},
errorState: null,
audioContext: null,
_booted: false,
_initialised: false,
_remote: {sessionID: null, sending: false},
_els: {
base: null,
toolTip: null,
prompt: {},
loading: {},
contextMenu: null
}
};
twirl.boot = function() {
if (twirl._booted) return;
twirl.audioContext = new AudioContext();
twirl._booted = true;
};
twirl.init = function() {
if (twirl._initialised) return;
var NoteData = function() {
var self = this;
this.data = null;
fetch("../twirl/notedata.json").then(function(r) {
r.json().then(function(j) {
self.data = j;
});
});
};
twirl.noteData = new NoteData();
// storage
twirl.storage.data = localStorage.getItem("twirl");
if (twirl.storage.data) {
twirl.storage.data = JSON.parse(twirl.storage.data);
} else {
twirl.storage.data = {};
}
// base
twirl._els.base = $("
").attr("id", "twirl").appendTo($("body"));
// tooltip
twirl._els.toolTip = $("").addClass("twirl_tooltip").appendTo(twirl._els.base);
// context menu
twirl._els.contextMenu = $("").addClass("twirl_contextmenu").appendTo(twirl._els.base);
// prompt
var p = twirl._els.prompt;
p.base = $("").attr("id", "twirl_prompt").appendTo(twirl._els.base);
$("").attr("id", "twirl_prompt_background").appendTo(p.base);
var promptInner = $("").attr("id", "twirl_prompt_inner").appendTo(p.base);
p.text = $("").attr("id", "twirl_prompt_text").appendTo(promptInner);
p.button = $("").attr("id", "twirl_prompt_button_text").text("OK");
p.buttonContainer = $("").attr("id", "twirl_prompt_button").append($("
")).append(p.button).appendTo(promptInner);
// loading
var l = twirl._els.loading;
l.base = $("").attr("id", "twirl_loading").appendTo(twirl._els.base);
$("").attr("id", "twirl_loading_background").appendTo(l.base);
var loadingInner = $("").attr("id", "twirl_loading_inner").appendTo(l.base);
l.text = $("").attr("id", "twirl_loading_text").text("Processing").appendTo(loadingInner);
l.percentContainer = $("").attr("id", "twirl_loading_percent").appendTo(loadingInner);
l.percent = $("").attr("id", "twirl_loading_percent_inner").appendTo(l.percentContainer);
// theme
if (twirl.storage.data.theme) {
twirl.setTheme(twirl.storage.data.theme, true);
}
twirl._initialised = true;
};
twirl.storage.save = function() {
localStorage.setItem("twirl", JSON.stringify(twirl.storage.data));
};
twirl.random = {
rgbColour: function() {
return "rgb(" + (Math.round(Math.random() * 50) + 205) + ","
+ (Math.round(Math.random() * 50) + 205) + ","
+ (Math.round(Math.random() * 50) + 205) + ")";
}
};
twirl.getSharedFiles = async function() {
var data = await fetch("../sounddb/data.json");
var json = await data.json();
return json;
};
twirl.createIcon = function(definition) {
var state = true;
var active = true;
function formatPath(i) {
return "../twirl/icon/" + i + ".svg";
}
var el = $("
");
if (definition.size) {
el.css("width", definition.size + "px");
}
var obj = {
el: el,
setState: function(tstate) {
if (!definition.icon2) return;
state = tstate;
if (state) {
el.attr("src", formatPath(definition.icon));
} else {
el.attr("src", formatPath(definition.icon2));
}
},
setActive: function(state) {
if (state) {
el.css("opacity", 1);
active = true;
} else {
el.css("opacity", 0.4);
active = false;
}
},
definition: definition
};
obj.click = function() {
definition.click(obj);
};
el.addClass("twirl_icon").css("opacity", 1).attr("src", formatPath(definition.icon)).on("mouseover", function(event){
var label = (!state && definition.label2) ? definition.label2 : definition.label;
twirl.tooltip.show(event, label);
}).on("mouseout", function(){
twirl.tooltip.hide();
}).click(function(el) {
if (active || definition.clickOnInactive) definition.click(obj);
});
return obj;
};
twirl.setTheme = function(name, noSave) {
var html = $("html");
if (html.attr("class")) {
for (let c of html.attr("class").split(/\s+/)) {
if (c.startsWith("theme")) {
html.removeClass(c);
}
}
}
html.addClass("theme" + name[0].toUpperCase() + name.substr(1).toLowerCase());
if (!noSave) {
twirl.storage.data.theme = name;
twirl.storage.save();
}
};
twirl.prompt = {
hide: function() {
twirl._els.prompt.base.hide();
},
show: function(text, onComplete, noButton) {
var p = twirl._els.prompt;
twirl.loading.hide();
p.text.empty();
if (typeof(text) == "string") {
p.text.text(text);
} else {
p.text.append(text);
}
if (!noButton) {
p.buttonContainer.show();
p.button.unbind().click(function(){
twirl.prompt.hide();
if (onComplete) onComplete();
});
} else {
p.buttonContainer.hide();
}
p.base.show();
}
};
twirl.loading = {
hide: function() {
$("body").css("cursor", "default");
twirl._els.loading.base.hide();
},
show: function(text, showPercent) {
var l = twirl._els.loading;
$("body").css("cursor", "wait");
l.text.text((text) ? text : "Processing");
if (showPercent) {
l.percentContainer.show();
} else {
l.percentContainer.hide();
}
l.base.show();
},
setPercent: function(percent) {
twirl._els.loading.percent.width(percent + "%");
}
};
twirl._setContextPosition = function(event, el, augmentations) {
var margin = 100;
if (!augmentations) augmentations = [0, 0];
if (event.clientX >= window.innerWidth - margin) {
el.css({right: margin + "px", left: "auto"});
} else {
el.css({right: "auto", left: (event.clientX + augmentations[0]) + "px"});
}
if (event.clientY >= window.innerHeight - margin) {
el.css({bottom: margin + "px", top: "auto"});
} else {
el.css({bottom: "auto", top: (event.clientY + augmentations[1]) + "px"});
}
};
twirl.contextMenu = {
show: function(event, data) {
event.preventDefault();
twirl._els.contextMenu.empty().unbind().on("mouseout", function(){
twirl._els.contextMenu.hide().off("mouseout");
});
for (let i in data) {
let d = data[i];
$("").addClass("twirl_contextmenu_item").text(d.name).click(function(){
twirl._els.contextMenu.hide().off("mouseout");
d.click();
}).appendTo(twirl._els.contextMenu).on("mouseout", function(e){
e.stopPropagation();
});
}
twirl._setContextPosition(event, twirl._els.contextMenu, [-10, -10]);
twirl._els.contextMenu.show();
return false;
}
};
twirl.tooltip = {
show: function(event, text, colour) {
if (!colour) colour = "#bbbbbb";
var el = twirl._els.toolTip;
el.html(text).css({opacity: 0.8, "background-color": colour});
twirl._setContextPosition(event, el, [20, -15]);
},
hide: function() {
twirl._els.toolTip.css("opacity", 0);
}
};
twirl.sendErrorState = async function(errorObj) {
if (twirl._remote.sending) return;
twirl._remote.sending = true;
if (typeof(errorObj) == "function") {
errorObj = errorObj();
}
errorObj.application = $("title").text();
var data = {
request_type: "LogError",
error: errorObj
};
if (twirl._remote.sessionID) {
data.session_id = twirl._remote.sessionID;
}
var resp = await fetch("https:///apps.csound.1bpm.net/service/", {
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data)
});
var json = await resp.json();
if (json.session_id && !twirl._remote.sessionID) {
twirl._remote.sessionID = json.session_id;
}
twirl._remote.sending = false;
};
twirl.errorHandler = function(text, onComplete, errorObj) {
var errorText = (!text) ? twirl.errorState : text;
if (!errorObj) errorObj = {};
errorObj.text = errorText;
//twirl.sendErrorState(errorObj);
twirl.prompt.show(errorText, onComplete);
twirl.errorState = null;
};
twirl.showSettings = function(host, settings, onThemeChange) {
var el = $("").css("font-size", "var(--fontSizeDefault)").append($("").text("Settings"));
var tb = $("");
$("").append(tb).appendTo(el);
var currentThemeIndex;
if (twirl.storage.data.theme) currentThemeIndex = twirl.themes.indexOf(twirl.storage.data.theme);
if (!currentThemeIndex) currentThemeIndex = 0;
var tpTheme = new twirl.transform.Parameter({
definition: {name: "Theme", options: twirl.themes, dfault: currentThemeIndex, fireChanges: false, automatable: false},
host: host,
onChange: function(val) {
twirl.setTheme(twirl.themes[val]);
if (onThemeChange) onThemeChange();
}
});
tb.append(tpTheme.getElementRow(true))
for (let s of settings) {
var value;
if (s.options && s.storageKey) {
if (host.storage[s.storageKey]) value = host.storage[s.storageKey];
if (value < 0) value = s.dfault;
} else if (s.storageKey) {
value = (host.storage[s.storageKey]) ? host.storage[s.storageKey] : s.dfault;
if (s.bool) {
s.min = 0;
s.max = 1;
s.step = 1;
}
} else {
value = s.dfault;
}
let param = new twirl.transform.Parameter({
definition: {
name: s.name,
description: s.description,
fireChanges: false, automatable: false,
min: s.min, max: s.max, step: s.step, dfault: value,
options: s.options, asvalue: s.asvalue
}, host: host, onChange: function(val) {
if (s.storageKey) {
host.storage[s.storageKey] = val;
host.saveStorage();
}
if (s.onChange) s.onChange(val);
}
});
tb.append(param.getElementRow(true));
}
twirl.prompt.show(el);
};
twirl.TopMenu = function(host, menuData, elTarget) {
var self = this;
var opened = false;
var keyHandlers = [];
function keyHandler(e) {
if (!host.visible) return;
var nodeType = e.target.nodeName.toLowerCase();
if (nodeType == "input" || nodeType == "textarea") return;
e.preventDefault();
for (let h of keyHandlers) {
if (
(h.key == e.key.toLowerCase()) &&
((!h.hasOwnProperty("ctrlKey") && !e.ctrlKey) || (h.ctrlKey && e.ctrlKey)) &&
((!h.hasOwnProperty("shiftKey") && !e.shiftKey) || (h.shiftKey && e.shiftKey)) &&
((!h.hasOwnProperty("altKey") && !e.altKey) || (h.altKey && e.altKey))
) {
if (h.hasOwnProperty("condition") && !h.condition(host)) {
return;
}
if (h.hasOwnProperty("keyCondition") && !h.keyCondition(host)) {
return;
}
return h.func(host);
}
}
}
function construct(data) {
if (!data) data = menuData;
let onPlayDisables = [];
elTarget.empty();
var elMenuBar = $("").addClass("topmenu").appendTo(elTarget);
for (let d of data) {
let elTopItem = $("").addClass("topmenu_item").text(d.name).appendTo(elMenuBar);
let elMenu = $("").addClass("topmenu_dropdown").appendTo(elTopItem);
let showConditions = [];
let onShows = [];
for (let c of d.contents) {
let elItem;
if (c.preset) {
if (c.preset == "divider") {
elItem = $("
").appendTo(elMenu);
}
} else {
elItem = $("").addClass("topmenu_dropdown_item").appendTo(elMenu);
if (typeof(c.name) == "function") {
onShows.push(function(){
elItem.text(c.name(host));
});
} else {
elItem.text(c.name);
}
if (c.click) elItem.click(function(){
if (c.condition && !c.condition(host)) return;
elMenu.hide();
opened = false;
c.click(host);
});
if (c.shortcut) {
$("").addClass("topmenu_dropdown_itemright").text(c.shortcut.name).appendTo(elItem);
var obj = {func: c.click};
Object.assign(obj, c.shortcut);
if (c.condition) obj.condition = c.condition;
if (c.keyCondition) obj.keyCondition = c.keyCondition;
delete obj.name;
keyHandlers.push(obj);
}
if (c.disableOnPlay) {
onPlayDisables.push(elItem);
}
}
if (c.condition) {
showConditions.push({el: elItem, func: c.condition});
}
}
function showMenu() {
for (let c of showConditions) {
if (c.func(host)) {
c.el.removeClass("topmenu_dropdown_item_disabled");
} else {
c.el.addClass("topmenu_dropdown_item_disabled");
}
}
for (let o of onShows) {
o(host);
}
elMenu.show();
}
elTopItem.on("mouseover", function(){
if (opened) {
setTimeout(function(){
opened = true;
}, 10);
showMenu();
}
}).on("click", function() {
opened = true;
showMenu();
}).on("mouseleave", function() {
elMenu.hide();
setTimeout(function(){
opened = false;
}, 5);
});
}
if (host.onPlays) {
host.onPlays.push(async function(playing){
for (let o of onPlayDisables) {
if (playing) {
o.addClass("topmenu_dropdown_item_disabled");
} else {
o.removeClass("topmenu_dropdown_item_disabled");
}
}
});
}
$("body").off("keydown", keyHandler).on("keydown", keyHandler);
}
construct();
};