commit
6493f70816
@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
@ -0,0 +1,11 @@
|
|||||||
|
const CONFIG = {
|
||||||
|
API_PORT: 23333,
|
||||||
|
|
||||||
|
API_LOCATION: "127.0.0.1",
|
||||||
|
|
||||||
|
STATIC_LOCATION: "./www",
|
||||||
|
STATIC_PORT: 8081, /* Warning: do not serve statics here. */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = CONFIG;
|
@ -0,0 +1,25 @@
|
|||||||
|
const fs = require('fs'),
|
||||||
|
config = require('./config');
|
||||||
|
http = require('http');
|
||||||
|
|
||||||
|
|
||||||
|
console.log("Starting static server on port "+config.STATIC_PORT);
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await http.createServer((req, res) => {
|
||||||
|
let url = req.url;
|
||||||
|
if(!req.url || req.url == "" || req.url == "/") url = "/index.html";
|
||||||
|
fs.readFile(config.STATIC_LOCATION + url, (err, data) => {
|
||||||
|
if(err) {
|
||||||
|
console.log("Failed to retreive requested url '"+url+"': "+err);
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end(JSON.stringify(err));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log("Writing "+url);
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).listen(config.STATIC_PORT);
|
||||||
|
})();
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"line-navigator": {
|
||||||
|
"version": "2.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/line-navigator/-/line-navigator-2.1.6.tgz",
|
||||||
|
"integrity": "sha1-s46Iq7vmEuqMBhEnugiu+dMPBg0="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
body, pre {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #1d1f21;
|
||||||
|
color: #c5c8c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font: bolder 28px Tahoma;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, aside, .popout, .page {
|
||||||
|
background-color: #282a2e;
|
||||||
|
border: 1px solid #393b3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
padding: 2px;
|
||||||
|
padding-left: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popout {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, aside {
|
||||||
|
display: table;
|
||||||
|
border-color: #282a2e;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
border-left-color: red;
|
||||||
|
border-left-style: solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selector {
|
||||||
|
padding: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
article header {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osufile {
|
||||||
|
width: 100%;
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
height: 12px;
|
||||||
|
left: -1px;
|
||||||
|
top: 2px;
|
||||||
|
margin-top: -3px;
|
||||||
|
width: 12px;
|
||||||
|
position:relative;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
border: 1px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
transition: color 1s;
|
||||||
|
-webkit-transition: color 1s;
|
||||||
|
-webkit-transition: background-color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse:hover {
|
||||||
|
color: orange;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed:hover {
|
||||||
|
background-color: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osufile:after {
|
||||||
|
cursor: pointer;
|
||||||
|
content: "X";
|
||||||
|
color: red;
|
||||||
|
transition: color 1s;
|
||||||
|
-webkit-transition: color 1s;
|
||||||
|
margin: -1px;
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osufile:hover:after {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#metadata {
|
||||||
|
right: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: fixed;
|
||||||
|
}
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,79 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel='stylesheet' type='text/css' href='css/main.css'></link>
|
||||||
|
<script src='js/vendor/file-wrapper.min.js'></script>
|
||||||
|
<script src='js/vendor/line-navigator.min.js'></script>
|
||||||
|
|
||||||
|
<script src='js/stage.js'></script>
|
||||||
|
<script src='js/util.js'></script>
|
||||||
|
<script src='js/osu.js'></script>
|
||||||
|
<script src='js/client.js'></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload='_onload()'>
|
||||||
|
<h1>osu!merge</h1>
|
||||||
|
<title>osu!merge</title>
|
||||||
|
<div class='indent'>
|
||||||
|
Merge <i>.osu</i> files together.
|
||||||
|
</div>
|
||||||
|
<hr></hr>
|
||||||
|
<div>
|
||||||
|
<div class='page'>
|
||||||
|
<b>Select .osu files</b>
|
||||||
|
<div id='selector'>
|
||||||
|
<div id='_file1' data-index='0' class='osufile' onclick='_removeFile("_file1")'>test</div>
|
||||||
|
<div id='_file2' data-index='0' class='osufile' onclick='_removeFile("_file2")'>test2</div>
|
||||||
|
</div>
|
||||||
|
Upload file: <input type='file' accept='text/plain' onchange='_readfile(event)'></input>
|
||||||
|
</div>
|
||||||
|
<div class='page' id='metadata'>
|
||||||
|
<div style='display:inline-block; width: 100%;'>
|
||||||
|
<b style='float:left;margin-top:1px;'>Metadata</b>
|
||||||
|
<span class='collapse' style='float: right; margin:1px;margin-left:5px;margin-bottom:4px;' data-collapse='meta-internal'></span>
|
||||||
|
</div>
|
||||||
|
<div id='meta-internal'>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<span class='collapse'></span>
|
||||||
|
General
|
||||||
|
</header>
|
||||||
|
<blockquote>
|
||||||
|
<div>Audio Filename: <span class='metadata' id='meta-audio-filename'></span></div>
|
||||||
|
<div>Lead-in: <span class='metadata' id='meta-audio-lead-in'></span></div>
|
||||||
|
<div>Preview Time: <span class='metadata' id='meta-preview-time'></span></div>
|
||||||
|
<div>Countdown: <span class='metadata' id='meta-countdown'></span></div>
|
||||||
|
<div>Default sample set: <span class='metadata' id='meta-sample-set'></span></div>
|
||||||
|
<div>Stack leniency: <span class='metadata' id='meta-stacking'></span></div>
|
||||||
|
<div>Mode: <span class='metadata' id='meta-mode'></span></div>
|
||||||
|
<div>Letterbox: <span class='metadata' id='meta-letterbox'></span></div>
|
||||||
|
<div>Widescreen: <span class='metadata' id='meta-widescreen'></span></div>
|
||||||
|
</blockquote>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<span class='collapse'></span>
|
||||||
|
Song
|
||||||
|
</header>
|
||||||
|
<blockquote>
|
||||||
|
<div>Title: <span class='metadata' id='meta-title'></span></div>
|
||||||
|
<div>Title (Raomanised): <span class='metadata' id='meta-title-ascii'></span></div>
|
||||||
|
<div>Artist: <span class='metadata' id='meta-artist'></span></div>
|
||||||
|
<div>Artist (Romanised): <span class='metadata' id='meta-artist-ascii'></span></div>
|
||||||
|
<div>Mapper: <span class='metadata' id='meta-mapper'></span></div>
|
||||||
|
<div>Difficulty: <span class='metadata' id='meta-difficulty'></span></div>
|
||||||
|
<div>Source: <span class='metadata' id='meta-source'></span></div>
|
||||||
|
<div>Tags: <span class='metadata' id='meta-tags'></span></div>
|
||||||
|
<div>ID: <span class='metadata' id='meta-beatmap-id'></span></div>
|
||||||
|
<div>Set ID: <span class='metadata' id='meta-beatmap-set-id'></span></div>
|
||||||
|
</blockquote>
|
||||||
|
</article>
|
||||||
|
Viewing:
|
||||||
|
<select>
|
||||||
|
<option value='Aggregate'>Aggregate</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style='margin: auto; margin-top: 5px; border-radius:5px; background: linear-gradient(90deg, green 25%, 25%, blue 50%, 50%, red 75%); height: 25px; width: 90%;'></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,72 @@
|
|||||||
|
var OSU_FILES = [];
|
||||||
|
|
||||||
|
const global = new Dispatcher();
|
||||||
|
|
||||||
|
function _removeFile(id) {
|
||||||
|
//TODO: Remove from bars too
|
||||||
|
let elem = document.getElementById(id);
|
||||||
|
const file_id = elem.dataset.index;
|
||||||
|
elem.remove();
|
||||||
|
|
||||||
|
if(file_id!==undefined && OSU_FILES[file_id])
|
||||||
|
{
|
||||||
|
global.signal("FILE_REMOVE", {id: file_id, osu: OSU_FILES[file_id]});
|
||||||
|
OSU_FILES[file_id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const READ_WHOLE_BUFFER = true;
|
||||||
|
|
||||||
|
function _readfile(event) {
|
||||||
|
var input = event.target;
|
||||||
|
|
||||||
|
if(READ_WHOLE_BUFFER) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = () => {
|
||||||
|
var osu = new OsuFile();
|
||||||
|
osu.LoadString(reader.result).then(result => {
|
||||||
|
console.log("Read version "+osu.version);
|
||||||
|
if(osu.okay()) {
|
||||||
|
//TODO: Push to read osu files
|
||||||
|
|
||||||
|
OSU_FILES.push(osu);
|
||||||
|
global.signal("FILE_ADD", {id: OSU_FILES.length-1, osu: osu});
|
||||||
|
} else {
|
||||||
|
var er = osu.errors;
|
||||||
|
console.log("Parsing .osu file failed: "+JSON.stringify(er));
|
||||||
|
//TODO: Signal error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsText(input.files[0]);
|
||||||
|
} else {
|
||||||
|
var nav = new LineNavigator(input.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _onload() {
|
||||||
|
|
||||||
|
// --- Set up collapsers --- //
|
||||||
|
const collapsers = document.getElementsByClassName('collapse');
|
||||||
|
|
||||||
|
for(const elem of collapsers) {
|
||||||
|
elem.addEventListener('click', function() {
|
||||||
|
let block;
|
||||||
|
if(this.dataset.collapse) {
|
||||||
|
block = document.getElementById(this.dataset.collapse);
|
||||||
|
}
|
||||||
|
else block = this.parentElement.nextSibling.nextSibling;
|
||||||
|
if(block.style.display === "none") {
|
||||||
|
removeClass(this, "collapsed");
|
||||||
|
block.style.display = "";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addClass(this, "collapsed");
|
||||||
|
block.style.display = "none";
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
// --- End collapsers --- //
|
||||||
|
}
|
@ -0,0 +1,334 @@
|
|||||||
|
function OsuFile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var OSU = OsuFile.prototype;
|
||||||
|
|
||||||
|
const get_bpm = len => (1.0 / len * 60000.00);
|
||||||
|
const get_beat = bpm => 60000.00 / bpm;
|
||||||
|
|
||||||
|
const get_sv = inverse => 100.00 / (-1.0 * inverse);
|
||||||
|
const get_inverse = sv => (sv / 100.00) * -1.0;
|
||||||
|
|
||||||
|
OSU.LoadString = async function(contents) {
|
||||||
|
const lines = contents.split(/(\n|\r|\f)+/).where(line => line && line.trim() != "");
|
||||||
|
|
||||||
|
var stage = new Stage();
|
||||||
|
stage.giveMany(lines);
|
||||||
|
stage.commit();
|
||||||
|
|
||||||
|
await this.LoadTokens(stage);
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.LoadTokens = async function(tokens) {
|
||||||
|
const stages = {
|
||||||
|
"General": new Stage(),
|
||||||
|
"Editor": new Stage(),
|
||||||
|
"Metadata": new Stage(),
|
||||||
|
"Difficulty": new Stage(),
|
||||||
|
"Events": new Stage(),
|
||||||
|
"TimingPoints": new Stage(),
|
||||||
|
"HitObjects": new Stage(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var line;
|
||||||
|
|
||||||
|
var group = null;
|
||||||
|
|
||||||
|
this.errors = [];
|
||||||
|
this.data = {};
|
||||||
|
|
||||||
|
this.futsuu(stages["General"]);
|
||||||
|
this.han(stages["Editor"]);
|
||||||
|
this.meta(stages["Metadata"]);
|
||||||
|
this.muzukashisa(stages["Difficulty"]);
|
||||||
|
this.jiken(stages["Events"]);
|
||||||
|
this.keiji(stages["TimingPoints"]);
|
||||||
|
this.maru(stages["HitObjects"]);
|
||||||
|
while(line = await tokens.take()) {
|
||||||
|
if(!this.version)
|
||||||
|
{
|
||||||
|
if(/^osu file format v\d+$/.test(line))
|
||||||
|
{
|
||||||
|
this.version = line.match(/\d+$/)[0];
|
||||||
|
}
|
||||||
|
else return ["no version present"];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(/^\/\//.test(line)) continue;
|
||||||
|
if(line.trim() === "") continue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let new_Group = line.match(/^\[(\w+)\]$/i);
|
||||||
|
console.log(new_Group);
|
||||||
|
if(new_Group && new_Group[1]) {
|
||||||
|
console.log("Setting new group `"+new_Group[1]+"' from old group "+group);
|
||||||
|
if(group) {
|
||||||
|
stages[group].commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
group = new_Group[1];
|
||||||
|
this.data[group] = {};
|
||||||
|
stages[group].give(group);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!group) continue;
|
||||||
|
if(!stages[group]) return ["unknown group "+group];
|
||||||
|
|
||||||
|
stages[group].give(line);
|
||||||
|
}
|
||||||
|
console.log("----END OF FILE");
|
||||||
|
if(group)
|
||||||
|
stages[group].commit();
|
||||||
|
|
||||||
|
for(const key of Object.keys(stages))
|
||||||
|
{
|
||||||
|
stages[key].forceClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.data);
|
||||||
|
return this.errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --- Parsers --- ///
|
||||||
|
|
||||||
|
OSU.futsuu = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var data = this.data[group];
|
||||||
|
var tok;
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
|
||||||
|
var match = tok.match(/^(\w+):\s(.*)$/i);
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
data[match[1]] = match[2];
|
||||||
|
}
|
||||||
|
else this.errors.push("futsuu: parsing line `"+tok+"' failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.han = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var data = this.data[group];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
var match = tok.match(/^(\w+):\s(.*)$/i);
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
if(match[1] === "Bookmarks")
|
||||||
|
{
|
||||||
|
data[match[1]] = match[2].split(/,/);
|
||||||
|
}
|
||||||
|
else data[match[1]] = match[2];
|
||||||
|
}
|
||||||
|
else this.errors.push("han: parsing line `"+tok+"' failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.meta = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var data = this.data[group];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
var match = tok.match(/^(\w+):\s?(.*)$/i);
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
if(match[1] === "Tags")
|
||||||
|
{
|
||||||
|
data[match[1]] = match[2].split(/\s/);
|
||||||
|
}
|
||||||
|
else data[match[1]] = match[2];
|
||||||
|
}
|
||||||
|
else this.errors.push("meta: parsing line `"+tok+"' failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.muzukashisa = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var data = this.data[group];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
var match = tok.match(/^(\w+):\s?(.*)$/i);
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
data[match[1]] = match[2];
|
||||||
|
}
|
||||||
|
else this.errors.push("muzukashisa: parsing line `"+tok+"' failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.jiken = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var data = this.data[group];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
var match = tok.match(/^(\w+|\d+),.+$/i);
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
//convenience?
|
||||||
|
const ar = JSON.parse("[" + tok + "]");
|
||||||
|
data[match[1]] = {time: ar[1], params: ar.slice(2)};
|
||||||
|
}
|
||||||
|
else this.errors.push("jiken: parsing line `"+tok+"' failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.keiji = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var _data = this.data[group] = [];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
var match = tok.match(/^(\d+),(-?\d+(?:\.\d+)?),(\d+),(\d+),(\d+),(\d+),([01]),(\d+)$/i);
|
||||||
|
let data = {};
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
data.time = match[1];
|
||||||
|
data.beatLength = match[2];
|
||||||
|
data.meter = match[3];
|
||||||
|
data.sampleSet = match[4];
|
||||||
|
data.sampleIndex = match[5];
|
||||||
|
data.volume = match[6];
|
||||||
|
data.uninherited = match[7] == "1";
|
||||||
|
data.effects = match[8];
|
||||||
|
}
|
||||||
|
else this.errors.push("keiji: parsing line `"+tok+"' failed.");
|
||||||
|
_data.push(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const BEZIER = 0;
|
||||||
|
const CENTRIPETAL_CATMULL_ROM = 1;
|
||||||
|
const LINEAR = 2;
|
||||||
|
const PERFECT_CIRCLE = 3;
|
||||||
|
const SLIDER_UNKNOWN = 4;
|
||||||
|
|
||||||
|
const parse_params = (params) => {
|
||||||
|
const re_slider_1 = /^(B|C|L|P)\|(.+)/i; //slider type
|
||||||
|
|
||||||
|
if(!params[0]) {
|
||||||
|
//Is circle
|
||||||
|
return {type: "circle"};
|
||||||
|
}
|
||||||
|
else if(re_slider_1 .test(params[0]))
|
||||||
|
{
|
||||||
|
//Is slider
|
||||||
|
const type = params[0].match(re_slider_1)[1].il_switch({'B': BEZIER,
|
||||||
|
'C': CENTRIPETAL_CATMULL_ROM,
|
||||||
|
'L': LINEAR,
|
||||||
|
'P': PERFECT_CIRCLE
|
||||||
|
}, SLIDER_UNKNOWN);
|
||||||
|
const points = params[0].split(/\|/).slice(1).map(x=> x.split(/:/));
|
||||||
|
const slides = params[1];
|
||||||
|
const length = params[2];
|
||||||
|
const edgeSounds = params[3].split(/\|/);
|
||||||
|
const edgeSets = params[4].split(/\|/).map(x=> x.split(/:/));
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "slider",
|
||||||
|
sliderType: type,
|
||||||
|
points: points,
|
||||||
|
slides: slides,
|
||||||
|
length: length,
|
||||||
|
edgeSounds: edgeSounds,
|
||||||
|
edgeSets: edgeSets,
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Spinner (or hold? TODO)
|
||||||
|
return {type: "spinner", endTime: params[0]};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.maru = async function(tokens) {
|
||||||
|
const group = await tokens.take();
|
||||||
|
if(group) {
|
||||||
|
var _data = this.data[group] = [];
|
||||||
|
var tok;
|
||||||
|
|
||||||
|
while(tok = await tokens.take()) {
|
||||||
|
let data = {};
|
||||||
|
var match = tok.match(/^(\d+),(\d+),(\d+),(\d+),(.+)$/i); //i honestly have no fucking idea what regex to get all these so we'll cheat
|
||||||
|
if(match && match[0])
|
||||||
|
{
|
||||||
|
data.x = match[1];
|
||||||
|
data.y = match[2];
|
||||||
|
data.time = match[2];
|
||||||
|
data.hitSound = match[3];
|
||||||
|
|
||||||
|
//Parse params and sample
|
||||||
|
if(/:i(?:.*\.wav)?$/.test(match[4]))
|
||||||
|
{
|
||||||
|
//Sample is there
|
||||||
|
let param = match[4].split(/,/);
|
||||||
|
data.hitSample = param.pop().split(/:/).where(/\d+/.test).associate([
|
||||||
|
"normalSet",
|
||||||
|
"additionalSet",
|
||||||
|
"index",
|
||||||
|
"volume",
|
||||||
|
"filename",
|
||||||
|
]);
|
||||||
|
data.params = parse_params(param);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//No sample, rest is params
|
||||||
|
data.params = parse_params(match[4].split(/,/));
|
||||||
|
data.hitSample = {
|
||||||
|
normalSet: "0",
|
||||||
|
additionalSet: "0",
|
||||||
|
index: "0",
|
||||||
|
volume: "0",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_data.push(data);
|
||||||
|
}
|
||||||
|
else this.errors.push("maru: parsing line `"+tok+"' failed.");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --- End parsers --- ///
|
||||||
|
|
||||||
|
OSU.okay = function() {
|
||||||
|
return !!this.version && this.errors.length<1;
|
||||||
|
};
|
||||||
|
|
||||||
|
OsuFile.Unserialise = (json) => {
|
||||||
|
|
||||||
|
if(typeof(json) === 'string') return OsuFile.Unserialise(JSON.parse(json));
|
||||||
|
|
||||||
|
var osu = new OsuFile();
|
||||||
|
osu.errors = [];
|
||||||
|
osu.version = json.version;
|
||||||
|
osu.data = json.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.serialise = function() {
|
||||||
|
return {version: this.version, data: this.data};
|
||||||
|
};
|
||||||
|
|
||||||
|
OSU.toString = function() {
|
||||||
|
return JSON.stringify(this.serialise());
|
||||||
|
};
|
@ -0,0 +1,106 @@
|
|||||||
|
function Stage(from) {
|
||||||
|
var base = this;
|
||||||
|
this.array = from || [];
|
||||||
|
this.over = false;
|
||||||
|
this.acceptInvalid = false;
|
||||||
|
var next = {
|
||||||
|
waiters: [],
|
||||||
|
wait: function() {
|
||||||
|
return new Promise(resolve => next.waiters.push(function() { resolve(); }));
|
||||||
|
},
|
||||||
|
signal: function() {
|
||||||
|
if(next.waiters.length>0) {
|
||||||
|
next.waiters.shift()();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flush: function() {
|
||||||
|
while(next.waiters.length>0) {
|
||||||
|
next.waiters.shift()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.take = async function() {
|
||||||
|
if(base.over && base.array.length<=0)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
if(base.array.length<=0)
|
||||||
|
await next.wait();
|
||||||
|
|
||||||
|
|
||||||
|
if(base.over && base.array.length<=0)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
return base.array.shift();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.poll = function() {
|
||||||
|
return base.array;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.takeNB = function() {
|
||||||
|
if(base.array.length<=0)
|
||||||
|
return null;
|
||||||
|
return base.array.shift();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.swallow0 = async function() {
|
||||||
|
var ar = [];
|
||||||
|
var token;
|
||||||
|
while(token = await base.take()) {
|
||||||
|
ar.push(token);
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.swallow = function(timeout) {
|
||||||
|
if(!timeout) return base.swallow0();
|
||||||
|
else {
|
||||||
|
return new Promise(function(_resolve,_reject) {
|
||||||
|
var running=true;
|
||||||
|
var resolve = function(v) {
|
||||||
|
if(running) _resolve(v);
|
||||||
|
running=false;
|
||||||
|
};
|
||||||
|
var reject= function(v) {
|
||||||
|
if(running) _reject(v);
|
||||||
|
running=false;
|
||||||
|
};
|
||||||
|
base.swallow0().then(resolve);
|
||||||
|
if(timeout>0)
|
||||||
|
setTimeout(()=> reject(new Error("swallow timeout reached ("+timeout+")")), timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.giveMany= function(values) {
|
||||||
|
for(let value of values)
|
||||||
|
base.give(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.give = function(value) {
|
||||||
|
if(!value && !base.acceptInvalid)
|
||||||
|
return;
|
||||||
|
base.array.push(value);
|
||||||
|
next.signal();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.commit = function() {
|
||||||
|
base.over = true;
|
||||||
|
next.flush();
|
||||||
|
|
||||||
|
return base;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.forceClose = function() {
|
||||||
|
if(base.over) return base;
|
||||||
|
else return base.commit();
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Stage.commitAll = (stages) => {
|
||||||
|
for(const st of stages)
|
||||||
|
st.commit();
|
||||||
|
};
|
@ -0,0 +1,93 @@
|
|||||||
|
function addGlobalStyle(text)
|
||||||
|
{
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.type='text/css';
|
||||||
|
if(style.styleSheet) {
|
||||||
|
style.styleSheet.cssText = text;
|
||||||
|
} else {
|
||||||
|
style.appendChild(document.createTextNode(text));
|
||||||
|
}
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(style);
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasClass(ele, cls)
|
||||||
|
{
|
||||||
|
return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addClass(ele, cls)
|
||||||
|
{
|
||||||
|
if(!hasClass(ele,cls)) ele.className += " "+cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.where = function(predicate) {
|
||||||
|
output = [];
|
||||||
|
for(const elem of this) {
|
||||||
|
if(predicate(elem)) output.push(elem);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.associate = function(keys) {
|
||||||
|
var output = {};
|
||||||
|
for(let i=0;i<this.length;i++)
|
||||||
|
{
|
||||||
|
output[keys[i]] = this[i];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
String.prototype.il_switch = function(obj, def) {
|
||||||
|
for(const key of obj)
|
||||||
|
{
|
||||||
|
if(this === key) return obj[key];
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
};
|
||||||
|
|
||||||
|
function removeClass(ele,cls)
|
||||||
|
{
|
||||||
|
if(hasClass(ele,cls))
|
||||||
|
{
|
||||||
|
const reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
|
||||||
|
ele.className = ele.className.replace(reg, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Dispatcher() {
|
||||||
|
this.hooks = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.prototype.hook = function(name, value) {
|
||||||
|
if(this.hooks[name]) {
|
||||||
|
this.hooks[name].push(value);
|
||||||
|
return {id: name, index: this.hooks[name].length-1};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.hooks[name] = [value];
|
||||||
|
return {id: name, index: 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Dispatcher.prototype.signal = function(name,value) {
|
||||||
|
if(this.hooks[name]) {
|
||||||
|
for(const hook of this.hooks[name])i
|
||||||
|
if(hook)
|
||||||
|
hook(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Dispatcher.prototype.unhook = function(hook) {
|
||||||
|
const name = hook.id;
|
||||||
|
if(name && hook.index && this.hooks[name]) {
|
||||||
|
this.hooks[name][hook.index] = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Dispatcher.prototype.clear = function(name) {
|
||||||
|
if(name) this.hooks[name] = null;
|
||||||
|
else this.hooks = {};
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/line-navigator/file-wrapper.min.js
|
@ -0,0 +1 @@
|
|||||||
|
../../../node_modules/line-navigator/line-navigator.min.js
|
Loading…
Reference in new issue