Files
crtr/Http/SkinEditor/app.js
2023-03-15 15:44:32 +08:00

568 lines
18 KiB
JavaScript

var messages = {};
var lang;
var literal = true;
var app;
var tv;
var tree;
var cel;
var clist;
var blocklists = {
"block.element": ["statement.select", "statement.obj", "statement.comp", "statement.prop"],
"block.root": ["statement.select", "statement.define"],
"block.select": ["statement.select.type", "statement.select.exp", "statement.select.anchor", "statement.select.atanchor"],
"block.exp.op.sep.vec": ["statement.exp.number"],
"block.exp.op.sep.arr": ["statement.exp.string"],
};
var ilid = 0;
var inputlists = {
"input.comp": ["anim", "image", "polysec", "rect", "scale3", "text"],
"input.prop": [
"pos", "rot", "scale",
"anim.delay", "anim.direction", "anim.duration", "anim.name", "anim.iteration",
"image.fit", "image.frame", "image.frames", "image.index", "image.shader",
"mesh.color", "mesh.opacity", "mesh.zindex",
"polysec.body", "polysec.head", "polysec.shape", "polysec.tail",
"scale3.border",
"sec.part", "sec.partial",
"sprite.bound", "sprite.pivot", "sprite.scale", "sprite.ui",
"text.frames", "text.size", "text.spacing", "text.value",
],
"input.type": ["chart", "group", "track", "note"],
};
var statementlists = {
"statement.comp": ["input.comp"],
"statement.define": ["input.ident", "exp"],
"statement.exp.cast.vector2number": ["exp.vector"],
"statement.exp.const.w": [],
"statement.exp.const.h": [],
"statement.exp.const.inf": [],
"statement.exp.const.null": [],
"statement.exp.const.true": [],
"statement.exp.const.false": [],
"statement.exp.literal.ident": ["input.ident"],
"statement.exp.literal.identforced": ["input.ident"],
"statement.exp.literal.number": ["input.number"],
"statement.exp.literal.string": ["input.string"],
"statement.exp.op.add": ["exp.number", "exp.number"],
"statement.exp.op.sub": ["exp.number", "exp.number"],
"statement.exp.op.mul": ["exp.number", "exp.number"],
"statement.exp.op.div": ["exp.number", "exp.number"],
"statement.exp.op.mod": ["exp.number", "exp.number"],
"statement.exp.op.add1": ["exp.number"],
"statement.exp.op.sub1": ["exp.number"],
"statement.exp.op.not": ["exp.number"],
"statement.exp.op.at": ["exp.number_or_vector", "exp.number"],
"statement.exp.op.lt": ["exp.number", "exp.number"],
"statement.exp.op.eq": ["exp.number", "exp.number"],
"statement.exp.op.gt": ["exp.number", "exp.number"],
"statement.exp.op.and": ["exp", "exp"],
"statement.exp.op.or": ["exp", "exp"],
"statement.exp.op.sep.vec": ["block.exp.op.sep.vec"],
"statement.exp.op.sep.arr": ["block.exp.op.sep.arr"],
"statement.exp.func.frame_seq": ["exp.string", "exp.number", "exp.number"],
"statement.exp.func.int": ["exp.number"],
"statement.exp.func.clamp": ["exp.number", "exp.number", "exp.number"],
"statement.exp.func.min": ["exp.number", "exp.number"],
"statement.exp.func.max": ["exp.number", "exp.number"],
"statement.exp.func.abs": ["exp.number"],
"statement.exp.func.interval": ["exp.number", "exp.number", "exp.number"],
"statement.exp.func.is": ["exp", "exp"],
"statement.exp.func.cubic_bezier": ["exp.number", "exp.number", "exp.number", "exp.number", "exp.number"],
"statement.exp.func.ease": ["exp.number"],
"statement.exp.func.ease_in": ["exp.number"],
"statement.exp.func.ease_out": ["exp.number"],
"statement.exp.func.ease_in_out": ["exp.number"],
"statement.exp.func.attack_timing": ["exp.number", "exp.number"],
"statement.exp.func.enter_timing": ["exp.number"],
"statement.exp.func.release_timing": ["exp.number", "exp.number"],
"statement.exp.func.leave_timing": ["exp.number"],
"statement.exp.func.contact_timing": ["exp.number", "exp.number"],
"statement.exp.func.screen_edge": ["exp.number"],
"statement.exp.func.anim": ["exp.number_or_vector", "exp.number_or_vector", "exp.number"],
"statement.exp.number": ["exp.number"],
"statement.exp.string": ["exp.string"],
"statement.obj": ["block.element"],
"statement.prop": ["input.prop", "exp"],
"statement.select": ["block.select", "block.element"],
"statement.select.anchor": ["input.identanchor"],
"statement.select.atanchor": ["input.identanchor"],
"statement.select.exp": ["exp.number"],
"statement.select.type": ["input.type"],
};
var explists = {
"exp": ["#exp.number", "#exp.vector", "#exp.string", "#exp.array", "#exp.identifier", "#exp.any"],
"exp.number_or_vector": ["#exp.number", "#exp.vector", "#exp.any"],
"exp.any": [
"statement.exp.literal.ident", "statement.exp.literal.identforced",
"statement.exp.op.and", "statement.exp.op.or"
],
"exp.array": [
"statement.exp.op.sep.arr",
"statement.exp.func.frame_seq",
"#exp.any"
],
"exp.identifier": [
"statement.exp.const.null",
"#exp.any"
],
"exp.number": [
"statement.exp.literal.number", "statement.exp.cast.vector2number",
"-exp.const", "statement.exp.const.w", "statement.exp.const.h", "statement.exp.const.inf", "statement.exp.const.true", "statement.exp.const.false",
"-exp.op", "statement.exp.op.add", "statement.exp.op.sub", "statement.exp.op.mul", "statement.exp.op.div", "statement.exp.op.mod",
"-", "statement.exp.op.add1", "statement.exp.op.sub1", "statement.exp.op.not",
"-", "statement.exp.op.at", "statement.exp.op.lt", "statement.exp.op.eq", "statement.exp.op.gt",
"-exp.func", "statement.exp.func.interval", "statement.exp.func.is",
"-exp.func.ctx", "statement.exp.func.int", "statement.exp.func.clamp", "statement.exp.func.min", "statement.exp.func.max", "statement.exp.func.abs",
"-exp.func.anim", "statement.exp.func.cubic_bezier", "statement.exp.func.ease", "statement.exp.func.ease_in", "statement.exp.func.ease_out", "statement.exp.func.ease_in_out",
"-exp.func.judge", "statement.exp.func.attack_timing", "statement.exp.func.enter_timing", "statement.exp.func.release_timing", "statement.exp.func.leave_timing", "statement.exp.func.contact_timing",
"#exp.any"
],
"exp.string": [
"statement.exp.literal.string",
"#exp.any"
],
"exp.vector": [
"statement.exp.op.sep.vec",
"statement.exp.func.screen_edge",
"statement.exp.func.anim",
"#exp.any"
],
};
window.onload = function () {
lang = navigator.language.replace("-", "_");
if (!messages[lang]) lang = "en_US";
app = $("#app");
tv = $("#treeview");
tree = $("#tree");
app.find("tbr").replaceWith(function () {
return msg($(this).text());
});
addAddButton(tree).on("click", function () {
if (clist) clist.remove();
});
tv.on("mousemove", null, null, onMouseMove);
$(".tgbtn").on("click", function () {
var btn = $(this);
btn.toggleClass("tgbtn-active");
onToggleButtonClick(btn.attr("id"), btn.hasClass("tgbtn-active"));
});
};
function addAddButton(el) {
return el.append($("<span></span>").addClass("btn btn-add").text("+").on("click", null, el, onAddButtonClick));
}
function addAddExpButton(el) {
return el.append($("<span></span>").addClass("btn btn-add").text("+").on("click", null, el, onAddExpButtonClick));
}
function addAddButtonLiteral(el) {
return el.append($("<span></span>").addClass("btn btn-add t-inline").text("+").on("click", null, el, onAddButtonLiteralClick));
}
function addDeleteButton(el) {
return el.append($("<span></span>").addClass("btn btn-delete").text("x").on("click", null, el, onDeleteButtonClick));
}
function addDeleteButtonLiteral(el) {
return el.append($("<span></span>").addClass("btn btn-delete t-inline").text("x").on("click", null, el, onDeleteButtonLiteralClick));
}
function onAddButtonClick(event) {
cel = el = event.data;
generateAddList(event, blocklists[el.attr("key")]);
}
function onAddExpButtonClick(event) {
cel = el = event.data;
generateAddExpList(event, el.attr("key"));
}
function onAddButtonLiteralClick(event) {
var el = event.data.children(".t-internal");
if (!el.hasClass("t-node"))
el = el.children(".t-node");
event.data = el;
onAddButtonClick(event);
}
function onDeleteButtonClick(event) {
var el = event.data.parents(".t-literal").first();
event.data.remove();
if (el) updateLiteral(el);
}
function onDeleteButtonLiteralClick(event) {
var el = event.data.parents(".t-literal").first();
fetchInternalStatements(el).eq(parseInt(event.data.attr("target"))).remove();
updateLiteral(el);
}
var tblist = {
"tb-debug": "mode-debug",
"tb-hide-add": "mode-hide-add",
"tb-hide-delete": "mode-hide-delete",
"tb-sort": "mode-sort",
};
function onToggleButtonClick(id, state) {
var cls = tblist[id];
if (state) tv.addClass(cls);
else tv.removeClass(cls);
if (cls == "mode-sort") {
var il = tv.find(".t-input");
var sl = tv.find(".t-statement").not(".t-inline").not(function () {
return $(this).parent().is(".t-exp");
});
if (state) {
il.each(function (i) {
$(il[i]).children("input").attr("disabled", "");
});
sl.each(function (i) {
$(sl[i]).on("click", null, $(sl[i]), onStatementSort);
});
}
else {
il.each(function (i) {
var el = $(il[i]);
if (!el.parents(".t-internal").length)
el.children("input").attr("disabled", null);
});
sl.each(function (i) {
$(sl[i]).off("click", null, onStatementSort);
});
tv.removeClass("state-grabbing");
if (grabbing) {
grabbing.removeClass("t-grabbing");
grabbing = null;
grabPointer.remove();
}
}
}
}
var grabbing;
var grabPointer;
function onStatementSort(event) {
event.stopPropagation();
if (grabbing) {
var l = grabbing.parent().children(".t-statement");
var i = getGrabPtrIndex(event.pageY + tv.scrollTop() - tv[0].offsetTop);
if (i == l.length)
grabbing.insertAfter(l.last());
else
grabbing.insertBefore(l.eq(i));
tv.removeClass("state-grabbing");
grabbing.removeClass("t-grabbing");
grabPointer.remove();
updateLiteral(grabbing.parents(".t-literal").first());
grabbing = null;
}
else {
grabbing = event.data;
grabbing.addClass("t-grabbing");
tv.addClass("state-grabbing");
grabPointer = $("<hr />").addClass("grab-ptr");
}
}
function onMouseMove(event) {
if (grabbing) {
var l = grabbing.parent().children(".t-statement");
var i = getGrabPtrIndex(event.pageY + tv.scrollTop() - tv[0].offsetTop);
if (i == l.length)
grabPointer.remove().insertAfter(l.last());
else
grabPointer.remove().insertBefore(l.eq(i));
}
}
function getGrabPtrIndex(y) {
var l = grabbing.parent().children(".t-statement");
var tl = l.map(function (i) {
return l[i].offsetTop - y;
}).toArray();
tl.push(l.last()[0].offsetTop + l.last().height() - y);
return ArrayUtil.binarySearch(tl, 0, function (a, b) {
return a - b;
}, true);
}
function generateAddList(event, items) {
event.stopPropagation();
if (clist) clist.remove();
var ul = $("<ul></ul>").addClass("t-list");
for (var i in items) {
var item = items[i];
ul.append($("<li></li>").addClass("t-li").attr("key", item).text(msg("list." + item)).on("click", null, item, onAddItemClick));
}
popup(event, ul);
}
function generateAddExpList(event, item) {
event.stopPropagation();
if (clist) clist.remove();
var ul = $("<ul></ul>").addClass("t-list t-exp-list");
(function addExpRecursive(it, depth) {
if (depth > 0) {
ul.append($("<li></li>").addClass("t-li-cat t-li-cat-" + depth.toString()).text(msg("list.category." + it)));
}
var items = explists[it];
for (var i in items) {
var item = items[i];
if (item[0] == "#") {
if (depth == 0) addExpRecursive(item.substring(1), 1);
}
else if (item == "-")
ul.append($("<li></li>").addClass("t-li-br"));
else if (item[0] == "-")
ul.append($("<li></li>").addClass("t-li-cat t-li-cat-" + (depth + 1).toString()).text(msg("list.category." + item.substring(1))));
else
ul.append($("<li></li>").addClass("t-li").attr("key", item).text(msg("list." + item)).on("click", null, item, onAddItemClick));
}
})(item, 0);
popup(event, ul);
}
function popup(event, ul) {
clist = $("<div></div>").addClass("t-list-container").append(ul);
app.append(clist);
ul.css("column-count", Math.max(1, Math.min(
Math.floor(0.6 * window.innerWidth / clist.outerWidth()),
Math.ceil((clist.outerHeight() + 256) / window.innerHeight)
)));
var left = event.pageX;
if (left + clist.outerWidth() > window.innerWidth)
left = Math.max(0, left - clist.outerWidth());
var top = event.pageY;
if (top + clist.outerHeight() > window.innerHeight)
top = Math.max(0, top - clist.outerHeight());
clist.css({
"left": left,
"top": top
});
clist.addClass("t-list-composed");
}
function generateInputList(el, key) {
var il = inputlists[key];
if (!il) return;
var id = "il-" + ilid.toString();
var dl = $("<datalist></datalist>").attr("id", id);
for (var i in il) {
var li = il[i];
dl.append($("<option />").attr({
"label": msg(key + "." + li),
"value": li,
}));
}
dl.insertAfter(el);
el.attr("list", id);
ilid++;
}
function onAddItemClick(event) {
var nel = create(event.data).insertBefore(cel.children(":last"));
if (cel.parents(".t-node").first().hasClass("t-literal"))
updateLiteral(cel.parents(".t-node").first());
var il = nel.find(".t-input");
il.each(function (i) {
var iel = $(il[i]);
Stretchy.resize(iel.children("input")[0]);
});
cel = null;
clist.remove();
}
function onInputLiteralUpdate(event) {
var ib = event.data.children("input");
var el = event.data.parents(".t-literal").first();
var lel = fetchInternalStatements(el).eq(parseInt(event.data.attr("target"))).children("[pid=\"" + event.data.attr("pid") + "\"]").children("input").val(ib.val());
Stretchy.resize(lel[0]);
}
function syncLiteral(iel) {
if (iel.attr("target") == -1) return;
var ib = iel.children("input");
var el = iel.parents(".t-literal").first();
ib.val(fetchInternalStatements(el).eq(parseInt(iel.attr("target"))).children("[pid=\"" + iel.attr("pid") + "\"]").children("input").val());
}
function fetchInternalStatements(el, key) {
var l = el.children(".t-internal");
if (!l.hasClass("t-node"))
l = l.children(".t-node").first();
if (key)
return l.children("[key=\"" + key + "\"]");
else
return l.children();
}
function create(key, target) {
var k0 = key.split(".")[0];
var flag = msg("literal." + key);
if (k0 == "input") flag = true;
if (target != null && literal && flag)
switch (k0) {
case "block":
return createBlockLiteral(key, target);
case "input":
return createInput(key, target);
case "statement":
return createStatementLiteral(key, target);
}
else
switch (k0) {
case "block":
return createBlock(key);
case "statement":
return createStatement(key);
case "exp":
return createExp(key);
}
}
function createBlock(key) {
var m = msg(key);
var b = addAddButton($("<div></div>").attr("key", key).addClass("t-node t-block"));
if (!m) return b;
else {
var n = $("<div></div>").addClass("t-node-sup t-block-sup").html(m);
n.children("tbr").replaceWith(b);
return n;
}
}
function createBlockLiteral(key, target) {
var o = createBlock(key).addClass("t-internal");
var el = $("<div></div>").attr({
"key": key,
"target": target.index()
}).addClass("t-node t-literal");
el.append(o);
updateLiteral(el);
return el;
}
function createExp(key) {
var m = msg(key);
var el = addAddExpButton($("<div></div>").attr("key", key).addClass("t-node t-exp t-inline").html(m));
return el;
}
function createInput(key, target) {
var el = $("<input />").attr("placeholder", msg(key + ".default"));
switch (key) {
case "input.number": el.attr("type", "number"); break;
default: el.attr("type", "text"); break;
}
var r = $("<span></span>").attr({
"key": key,
"target": target.index()
}).addClass("t-node t-input t-inline").append(el);
generateInputList(el, key);
applyResizeHandler(el);
el.on("change input", null, r, onInputLiteralUpdate);
return r;
}
function createStatement(key) {
var r = msg(key);
var el = addDeleteButton($("<div></div>").attr("key", key).addClass("t-node t-statement").html(r));
el.children("tbr").replaceWith(function () {
var m = $(this).text();
var id = parseInt(m);
if (isNaN(id))
return create(m, $(this).parent());
else
return create(statementlists[key][id], $(this).parent()).attr("pid", id);
});
return el;
}
function createStatementLiteral(key, target) {
var r = msg("literal." + key);
var el = addDeleteButtonLiteral($("<div></div>").attr({
"key": key,
"target": target.index()
}).addClass("t-node t-statement t-inline").html(r));
el.children("tbr").replaceWith(function () {
var m = $(this).text();
var id = parseInt(m);
if (isNaN(id))
return create(m, target);
else
return create(statementlists[key][id], target).attr("pid", id);
});
return el;
}
function applyResizeHandler(el) {
el.on("change input", function () {
Stretchy.resize(this);
});
}
function updateLiteral(el) {
var key = el.attr("key");
var m = msg("literal." + key);
var o = el.children(".t-internal");
tv.append(o); // Stashes the supplement blocks to prevent the event listeners from being detached
addAddButtonLiteral(el.html(m));
el.append(o); // Restores the blocks
el.children("tbr").replaceWith(function () {
var m = $(this).text().split("?");
var def;
if (m.length > 1) {
def = m[1];
m = m[0];
}
else
m = m[0];
var l = fetchInternalStatements(el, m);
var s = l.map(function (si) {
return create(m, l.eq(si));
});
return conjAnd(s, def);
});
var il = el.find(".t-input");
il.each(function (i) {
var iel = $(il[i]);
if (iel.parents(".t-internal").length || tv.hasClass("mode-sort"))
iel.children("input").attr("disabled", "");
syncLiteral(iel);
Stretchy.resize(iel.children("input")[0]);
});
var cl = el.contents();
for (var t in cl) {
var ci = cl[t];
if (ci.nodeName == "#text" && ci.nodeValue == " ") {
ci.remove();
}
}
}
function conjAnd(list, def) {
var el = $("<div></div>").addClass("t-conj t-inline");
for (var i = 0; i < list.length; i++)
el.append(list[i]);
if (list.length == 0)
el.text(def);
return el;
}
function msg(key) {
var m = messages[lang][key];
if (!m) return null;
return m.replace(/{(.*?)}/g, "<tbr>$1</tbr>");
}