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