lingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
1,571 lines • 265 kB
JavaScript
import { handleStopPropagation } from "../../engine/hotkeys";
import { emitEditorEdit } from "../../events/onEditorEdit";
import { tweakpaneChangePtr } from "../../pointers/tweakpaneChangePtr";
import { tweakpaneDownPtr } from "../../pointers/tweanpaneDownPtr";
const handleDown = (ev) => {
handleStopPropagation(ev);
tweakpaneDownPtr[0] = true;
queueMicrotask(() => {
const [key, value] = tweakpaneChangePtr[0];
emitEditorEdit({ phase: "start", key, value });
});
};
const handleUp = (ev) => {
handleStopPropagation(ev);
queueMicrotask(() => {
tweakpaneDownPtr[0] = false;
const [key, value] = tweakpaneChangePtr[0];
emitEditorEdit({ phase: "end", key, value });
});
};
/***
* A simple semantic versioning perser.
*/
class Semver {
/**
* @hidden
*/
constructor(text) {
const [core, prerelease] = text.split("-");
const coreComps = core.split(".");
this.major = parseInt(coreComps[0], 10);
this.minor = parseInt(coreComps[1], 10);
this.patch = parseInt(coreComps[2], 10);
this.prerelease =
prerelease !== null && prerelease !== void 0 ? prerelease : null;
}
toString() {
const core = [this.major, this.minor, this.patch].join(".");
return this.prerelease !== null
? [core, this.prerelease].join("-")
: core;
}
}
class BladeApi {
constructor(controller) {
this.controller_ = controller;
}
get element() {
return this.controller_.view.element;
}
get disabled() {
return this.controller_.viewProps.get("disabled");
}
set disabled(disabled) {
this.controller_.viewProps.set("disabled", disabled);
}
get hidden() {
return this.controller_.viewProps.get("hidden");
}
set hidden(hidden) {
this.controller_.viewProps.set("hidden", hidden);
}
dispose() {
this.controller_.viewProps.set("disposed", true);
}
}
class TpEvent {
constructor(target) {
this.target = target;
}
}
class TpChangeEvent extends TpEvent {
constructor(target, value, presetKey, last) {
super(target);
this.value = value;
this.presetKey = presetKey;
this.last = last !== null && last !== void 0 ? last : true;
}
}
class TpUpdateEvent extends TpEvent {
constructor(target, value, presetKey) {
super(target);
this.value = value;
this.presetKey = presetKey;
}
}
class TpFoldEvent extends TpEvent {
constructor(target, expanded) {
super(target);
this.expanded = expanded;
}
}
class TpTabSelectEvent extends TpEvent {
constructor(target, index) {
super(target);
this.index = index;
}
}
function forceCast(v) {
return v;
}
function isEmpty(value) {
return value === null || value === undefined;
}
function deepEqualsArray(a1, a2) {
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
const CREATE_MESSAGE_MAP = {
alreadydisposed: () => "View has been already disposed",
invalidparams: (context) => `Invalid parameters for '${context.name}'`,
nomatchingcontroller: (context) => `No matching controller for '${context.key}'`,
nomatchingview: (context) => `No matching view for '${JSON.stringify(context.params)}'`,
notbindable: () => `Value is not bindable`,
propertynotfound: (context) => `Property '${context.name}' not found`,
shouldneverhappen: () => "This error should never happen"
};
class TpError {
constructor(config) {
var _a;
this.message =
(_a = CREATE_MESSAGE_MAP[config.type](forceCast(config.context))) !== null && _a !== void 0
? _a
: "Unexpected error";
this.name = this.constructor.name;
this.stack = new Error(this.message).stack;
this.type = config.type;
}
static alreadyDisposed() {
return new TpError({ type: "alreadydisposed" });
}
static notBindable() {
return new TpError({
type: "notbindable"
});
}
static propertyNotFound(name) {
return new TpError({
type: "propertynotfound",
context: {
name: name
}
});
}
static shouldNeverHappen() {
return new TpError({ type: "shouldneverhappen" });
}
}
class BindingTarget {
constructor(obj, key, opt_id) {
this.obj_ = obj;
this.key_ = key;
this.presetKey_ = opt_id !== null && opt_id !== void 0 ? opt_id : key;
}
static isBindable(obj) {
if (obj === null) {
return false;
}
if (typeof obj !== "object") {
return false;
}
return true;
}
get key() {
return this.key_;
}
get presetKey() {
return this.presetKey_;
}
read() {
return this.obj_[this.key_];
}
write(value) {
this.obj_[this.key_] = value;
}
writeProperty(name, value) {
const valueObj = this.read();
if (!BindingTarget.isBindable(valueObj)) {
throw TpError.notBindable();
}
if (!(name in valueObj)) {
throw TpError.propertyNotFound(name);
}
valueObj[name] = value;
}
}
class ButtonApi extends BladeApi {
get label() {
return this.controller_.props.get("label");
}
set label(label) {
this.controller_.props.set("label", label);
}
get title() {
var _a;
return (_a = this.controller_.valueController.props.get("title")) !==
null && _a !== void 0
? _a
: "";
}
set title(title) {
this.controller_.valueController.props.set("title", title);
}
on(eventName, handler) {
const bh = handler.bind(this);
const emitter = this.controller_.valueController.emitter;
emitter.on(eventName, () => {
bh(new TpEvent(this));
});
return this;
}
}
class Emitter {
constructor() {
this.observers_ = {};
}
on(eventName, handler) {
let observers = this.observers_[eventName];
if (!observers) {
observers = this.observers_[eventName] = [];
}
observers.push({
handler: handler
});
return this;
}
off(eventName, handler) {
const observers = this.observers_[eventName];
if (observers) {
this.observers_[eventName] = observers.filter((observer) => {
return observer.handler !== handler;
});
}
return this;
}
emit(eventName, event) {
const observers = this.observers_[eventName];
if (!observers) {
return;
}
observers.forEach((observer) => {
observer.handler(event);
});
}
}
const PREFIX = "tp";
function ClassName(viewName) {
const fn = (opt_elementName, opt_modifier) => {
return [
PREFIX,
"-",
viewName,
"v",
opt_elementName ? `_${opt_elementName}` : "",
opt_modifier ? `-${opt_modifier}` : ""
].join("");
};
return fn;
}
function compose(h1, h2) {
return (input) => h2(h1(input));
}
function extractValue(ev) {
return ev.rawValue;
}
function bindValue(value, applyValue) {
value.emitter.on("change", compose(extractValue, applyValue));
applyValue(value.rawValue);
}
function bindValueMap(valueMap, key, applyValue) {
bindValue(valueMap.value(key), applyValue);
}
function applyClass(elem, className, active) {
if (active) {
elem.classList.add(className);
}
else {
elem.classList.remove(className);
}
}
function valueToClassName(elem, className) {
return (value) => {
applyClass(elem, className, value);
};
}
function bindValueToTextContent(value, elem) {
bindValue(value, (text) => {
elem.textContent = text !== null && text !== void 0 ? text : "";
});
}
const className$q = ClassName("btn");
class ButtonView {
constructor(doc, config) {
this.element = doc.createElement("div");
this.element.classList.add(className$q());
config.viewProps.bindClassModifiers(this.element);
const buttonElem = doc.createElement("button");
buttonElem.classList.add(className$q("b"));
config.viewProps.bindDisabled(buttonElem);
this.element.appendChild(buttonElem);
this.buttonElement = buttonElem;
const titleElem = doc.createElement("div");
titleElem.classList.add(className$q("t"));
bindValueToTextContent(config.props.value("title"), titleElem);
this.buttonElement.appendChild(titleElem);
}
}
class ButtonController {
constructor(doc, config) {
this.emitter = new Emitter();
this.onClick_ = this.onClick_.bind(this);
this.props = config.props;
this.viewProps = config.viewProps;
this.view = new ButtonView(doc, {
props: this.props,
viewProps: this.viewProps
});
this.view.buttonElement.addEventListener("click", this.onClick_);
}
onClick_() {
this.emitter.emit("click", {
sender: this
});
}
}
class BoundValue {
constructor(initialValue, config) {
var _a;
this.constraint_ =
config === null || config === void 0 ? void 0 : config.constraint;
this.equals_ =
(_a =
config === null || config === void 0
? void 0
: config.equals) !== null && _a !== void 0
? _a
: (v1, v2) => v1 === v2;
this.emitter = new Emitter();
this.rawValue_ = initialValue;
}
get constraint() {
return this.constraint_;
}
get rawValue() {
return this.rawValue_;
}
set rawValue(rawValue) {
this.setRawValue(rawValue, {
forceEmit: false,
last: true
});
}
setRawValue(rawValue, options) {
const opts = options !== null && options !== void 0
? options
: {
forceEmit: false,
last: true
};
const constrainedValue = this.constraint_
? this.constraint_.constrain(rawValue)
: rawValue;
const changed = !this.equals_(this.rawValue_, constrainedValue);
if (!changed && !opts.forceEmit) {
return;
}
this.emitter.emit("beforechange", {
sender: this
});
this.rawValue_ = constrainedValue;
this.emitter.emit("change", {
options: opts,
rawValue: constrainedValue,
sender: this
});
}
}
class PrimitiveValue {
constructor(initialValue) {
this.emitter = new Emitter();
this.value_ = initialValue;
}
get rawValue() {
return this.value_;
}
set rawValue(value) {
this.setRawValue(value, {
forceEmit: false,
last: true
});
}
setRawValue(value, options) {
const opts = options !== null && options !== void 0
? options
: {
forceEmit: false,
last: true
};
if (this.value_ === value && !opts.forceEmit) {
return;
}
this.emitter.emit("beforechange", {
sender: this
});
this.value_ = value;
this.emitter.emit("change", {
options: opts,
rawValue: this.value_,
sender: this
});
}
}
function createValue(initialValue, config) {
const constraint = config === null || config === void 0 ? void 0 : config.constraint;
const equals = config === null || config === void 0 ? void 0 : config.equals;
if (!constraint && !equals) {
return new PrimitiveValue(initialValue);
}
return new BoundValue(initialValue, config);
}
class ValueMap {
constructor(valueMap) {
this.emitter = new Emitter();
this.valMap_ = valueMap;
for (const key in this.valMap_) {
const v = this.valMap_[key];
v.emitter.on("change", () => {
this.emitter.emit("change", {
key: key,
sender: this
});
});
}
}
static createCore(initialValue) {
const keys = Object.keys(initialValue);
return keys.reduce((o, key) => {
return Object.assign(o, {
[key]: createValue(initialValue[key])
});
}, {});
}
static fromObject(initialValue) {
const core = this.createCore(initialValue);
return new ValueMap(core);
}
get(key) {
return this.valMap_[key].rawValue;
}
set(key, value) {
this.valMap_[key].rawValue = value;
}
value(key) {
return this.valMap_[key];
}
}
function parseObject(value, keyToParserMap) {
const keys = Object.keys(keyToParserMap);
const result = keys.reduce((tmp, key) => {
if (tmp === undefined) {
return undefined;
}
const parser = keyToParserMap[key];
const result = parser(value[key]);
return result.succeeded
? Object.assign(Object.assign({}, tmp), { [key]: result.value })
: undefined;
}, {});
return forceCast(result);
}
function parseArray(value, parseItem) {
return value.reduce((tmp, item) => {
if (tmp === undefined) {
return undefined;
}
const result = parseItem(item);
if (!result.succeeded || result.value === undefined) {
return undefined;
}
return [...tmp, result.value];
}, []);
}
function isObject(value) {
if (value === null) {
return false;
}
return typeof value === "object";
}
function createParamsParserBuilder(parse) {
return (optional) => (v) => {
if (!optional && v === undefined) {
return {
succeeded: false,
value: undefined
};
}
if (optional && v === undefined) {
return {
succeeded: true,
value: undefined
};
}
const result = parse(v);
return result !== undefined
? {
succeeded: true,
value: result
}
: {
succeeded: false,
value: undefined
};
};
}
function createParamsParserBuilders(optional) {
return {
custom: (parse) => createParamsParserBuilder(parse)(optional),
boolean: createParamsParserBuilder((v) => typeof v === "boolean" ? v : undefined)(optional),
number: createParamsParserBuilder((v) => typeof v === "number" ? v : undefined)(optional),
string: createParamsParserBuilder((v) => typeof v === "string" ? v : undefined)(optional),
function: createParamsParserBuilder((v) => typeof v === "function" ? v : undefined)(optional),
constant: (value) => createParamsParserBuilder((v) => (v === value ? value : undefined))(optional),
raw: createParamsParserBuilder((v) => v)(optional),
object: (keyToParserMap) => createParamsParserBuilder((v) => {
if (!isObject(v)) {
return undefined;
}
return parseObject(v, keyToParserMap);
})(optional),
array: (itemParser) => createParamsParserBuilder((v) => {
if (!Array.isArray(v)) {
return undefined;
}
return parseArray(v, itemParser);
})(optional)
};
}
const ParamsParsers = {
optional: createParamsParserBuilders(true),
required: createParamsParserBuilders(false)
};
function parseParams(value, keyToParserMap) {
const result = ParamsParsers.required.object(keyToParserMap)(value);
return result.succeeded ? result.value : undefined;
}
function disposeElement(elem) {
if (elem && elem.parentElement) {
elem.parentElement.removeChild(elem);
}
return null;
}
function getAllBladePositions() {
return ["veryfirst", "first", "last", "verylast"];
}
const className$p = ClassName("");
const POS_TO_CLASS_NAME_MAP = {
veryfirst: "vfst",
first: "fst",
last: "lst",
verylast: "vlst"
};
class BladeController {
constructor(config) {
this.parent_ = null;
this.blade = config.blade;
this.view = config.view;
this.viewProps = config.viewProps;
const elem = this.view.element;
this.blade.value("positions").emitter.on("change", () => {
getAllBladePositions().forEach((pos) => {
elem.classList.remove(className$p(undefined, POS_TO_CLASS_NAME_MAP[pos]));
});
this.blade.get("positions").forEach((pos) => {
elem.classList.add(className$p(undefined, POS_TO_CLASS_NAME_MAP[pos]));
});
});
this.viewProps.handleDispose(() => {
disposeElement(elem);
});
}
get parent() {
return this.parent_;
}
}
const SVG_NS = "http://www.w3.org/2000/svg";
function forceReflow(element) {
element.offsetHeight;
}
function disableTransitionTemporarily(element, callback) {
const t = element.style.transition;
element.style.transition = "none";
callback();
element.style.transition = t;
}
function supportsTouch(doc) {
return doc.ontouchstart !== undefined;
}
function getGlobalObject() {
return new Function("return this")();
}
function getWindowDocument() {
const globalObj = forceCast(getGlobalObject());
return globalObj.document;
}
function getCanvasContext(canvasElement) {
const win = canvasElement.ownerDocument.defaultView;
if (!win) {
return null;
}
const isBrowser = "document" in win;
return isBrowser ? canvasElement.getContext("2d") : null;
}
const ICON_ID_TO_INNER_HTML_MAP = {
check: '<path d="M2 8l4 4l8 -8"/>',
dropdown: '<path d="M5 7h6l-3 3 z"/>',
p2dpad: '<path d="M8 4v8"/><path d="M4 8h8"/><circle cx="12" cy="12" r="1.2"/>'
};
function createSvgIconElement(document, iconId) {
const elem = document.createElementNS(SVG_NS, "svg");
elem.innerHTML = ICON_ID_TO_INNER_HTML_MAP[iconId];
return elem;
}
function insertElementAt(parentElement, element, index) {
parentElement.insertBefore(element, parentElement.children[index]);
}
function removeElement(element) {
if (element.parentElement) {
element.parentElement.removeChild(element);
}
}
function removeChildElements(element) {
while (element.children.length > 0) {
element.removeChild(element.children[0]);
}
}
function removeChildNodes(element) {
while (element.childNodes.length > 0) {
element.removeChild(element.childNodes[0]);
}
}
function findNextTarget(ev) {
if (ev.relatedTarget) {
return forceCast(ev.relatedTarget);
}
if ("explicitOriginalTarget" in ev) {
return ev.explicitOriginalTarget;
}
return null;
}
const className$o = ClassName("lbl");
function createLabelNode(doc, label) {
const frag = doc.createDocumentFragment();
const lineNodes = label.split("\n").map((line) => {
return doc.createTextNode(line);
});
lineNodes.forEach((lineNode, index) => {
if (index > 0) {
frag.appendChild(doc.createElement("br"));
}
frag.appendChild(lineNode);
});
return frag;
}
class LabelView {
constructor(doc, config) {
this.element = doc.createElement("div");
this.element.classList.add(className$o());
config.viewProps.bindClassModifiers(this.element);
const labelElem = doc.createElement("div");
labelElem.classList.add(className$o("l"));
bindValueMap(config.props, "label", (value) => {
if (isEmpty(value)) {
this.element.classList.add(className$o(undefined, "nol"));
}
else {
this.element.classList.remove(className$o(undefined, "nol"));
removeChildNodes(labelElem);
labelElem.appendChild(createLabelNode(doc, value));
}
});
this.element.appendChild(labelElem);
this.labelElement = labelElem;
const valueElem = doc.createElement("div");
valueElem.classList.add(className$o("v"));
this.element.appendChild(valueElem);
this.valueElement = valueElem;
}
}
class LabelController extends BladeController {
constructor(doc, config) {
const viewProps = config.valueController.viewProps;
super(Object.assign(Object.assign({}, config), {
view: new LabelView(doc, {
props: config.props,
viewProps: viewProps
}),
viewProps: viewProps
}));
this.props = config.props;
this.valueController = config.valueController;
this.view.valueElement.appendChild(this.valueController.view.element);
}
}
const ButtonBladePlugin = {
id: "button",
type: "blade",
accept(params) {
const p = ParamsParsers;
const result = parseParams(params, {
title: p.required.string,
view: p.required.constant("button"),
label: p.optional.string
});
return result ? { params: result } : null;
},
controller(args) {
return new LabelController(args.document, {
blade: args.blade,
props: ValueMap.fromObject({
label: args.params.label
}),
valueController: new ButtonController(args.document, {
props: ValueMap.fromObject({
title: args.params.title
}),
viewProps: args.viewProps
})
});
},
api(args) {
if (!(args.controller instanceof LabelController)) {
return null;
}
if (!(args.controller.valueController instanceof ButtonController)) {
return null;
}
return new ButtonApi(args.controller);
}
};
class ValueBladeController extends BladeController {
constructor(config) {
super(config);
this.value = config.value;
}
}
function createBlade() {
return new ValueMap({
positions: createValue([], {
equals: deepEqualsArray
})
});
}
class Foldable extends ValueMap {
constructor(valueMap) {
super(valueMap);
}
static create(expanded) {
const coreObj = {
completed: true,
expanded: expanded,
expandedHeight: null,
shouldFixHeight: false,
temporaryExpanded: null
};
const core = ValueMap.createCore(coreObj);
return new Foldable(core);
}
get styleExpanded() {
var _a;
return (_a = this.get("temporaryExpanded")) !== null && _a !== void 0
? _a
: this.get("expanded");
}
get styleHeight() {
if (!this.styleExpanded) {
return "0";
}
const exHeight = this.get("expandedHeight");
if (this.get("shouldFixHeight") && !isEmpty(exHeight)) {
return `${exHeight}px`;
}
return "auto";
}
bindExpandedClass(elem, expandedClassName) {
const onExpand = () => {
const expanded = this.styleExpanded;
if (expanded) {
elem.classList.add(expandedClassName);
}
else {
elem.classList.remove(expandedClassName);
}
};
bindValueMap(this, "expanded", onExpand);
bindValueMap(this, "temporaryExpanded", onExpand);
}
cleanUpTransition() {
this.set("shouldFixHeight", false);
this.set("expandedHeight", null);
this.set("completed", true);
}
}
function computeExpandedFolderHeight(folder, containerElement) {
let height = 0;
disableTransitionTemporarily(containerElement, () => {
folder.set("expandedHeight", null);
folder.set("temporaryExpanded", true);
forceReflow(containerElement);
height = containerElement.clientHeight;
folder.set("temporaryExpanded", null);
forceReflow(containerElement);
});
return height;
}
function applyHeight(foldable, elem) {
elem.style.height = foldable.styleHeight;
}
function bindFoldable(foldable, elem) {
foldable.value("expanded").emitter.on("beforechange", () => {
foldable.set("completed", false);
if (isEmpty(foldable.get("expandedHeight"))) {
foldable.set("expandedHeight", computeExpandedFolderHeight(foldable, elem));
}
foldable.set("shouldFixHeight", true);
forceReflow(elem);
});
foldable.emitter.on("change", () => {
applyHeight(foldable, elem);
});
applyHeight(foldable, elem);
elem.addEventListener("transitionend", (ev) => {
if (ev.propertyName !== "height") {
return;
}
foldable.cleanUpTransition();
});
}
class RackLikeApi extends BladeApi {
constructor(controller, rackApi) {
super(controller);
this.rackApi_ = rackApi;
}
}
function addButtonAsBlade(api, params) {
return api.addBlade(Object.assign(Object.assign({}, params), { view: "button" }));
}
function addFolderAsBlade(api, params) {
return api.addBlade(Object.assign(Object.assign({}, params), { view: "folder" }));
}
function addSeparatorAsBlade(api, opt_params) {
const params = opt_params !== null && opt_params !== void 0 ? opt_params : {};
return api.addBlade(Object.assign(Object.assign({}, params), { view: "separator" }));
}
function addTabAsBlade(api, params) {
return api.addBlade(Object.assign(Object.assign({}, params), { view: "tab" }));
}
class NestedOrderedSet {
constructor(extract) {
this.emitter = new Emitter();
this.items_ = [];
this.cache_ = new Set();
this.onSubListAdd_ = this.onSubListAdd_.bind(this);
this.onSubListRemove_ = this.onSubListRemove_.bind(this);
this.extract_ = extract;
}
get items() {
return this.items_;
}
allItems() {
return Array.from(this.cache_);
}
find(callback) {
for (const item of this.allItems()) {
if (callback(item)) {
return item;
}
}
return null;
}
includes(item) {
return this.cache_.has(item);
}
add(item, opt_index) {
if (this.includes(item)) {
throw TpError.shouldNeverHappen();
}
const index = opt_index !== undefined ? opt_index : this.items_.length;
this.items_.splice(index, 0, item);
this.cache_.add(item);
const subList = this.extract_(item);
if (subList) {
subList.emitter.on("add", this.onSubListAdd_);
subList.emitter.on("remove", this.onSubListRemove_);
subList.allItems().forEach((item) => {
this.cache_.add(item);
});
}
this.emitter.emit("add", {
index: index,
item: item,
root: this,
target: this
});
}
remove(item) {
const index = this.items_.indexOf(item);
if (index < 0) {
return;
}
this.items_.splice(index, 1);
this.cache_.delete(item);
const subList = this.extract_(item);
if (subList) {
subList.emitter.off("add", this.onSubListAdd_);
subList.emitter.off("remove", this.onSubListRemove_);
}
this.emitter.emit("remove", {
index: index,
item: item,
root: this,
target: this
});
}
onSubListAdd_(ev) {
this.cache_.add(ev.item);
this.emitter.emit("add", {
index: ev.index,
item: ev.item,
root: this,
target: ev.target
});
}
onSubListRemove_(ev) {
this.cache_.delete(ev.item);
this.emitter.emit("remove", {
index: ev.index,
item: ev.item,
root: this,
target: ev.target
});
}
}
class InputBindingApi extends BladeApi {
constructor(controller) {
super(controller);
this.onBindingChange_ = this.onBindingChange_.bind(this);
this.emitter_ = new Emitter();
this.controller_.binding.emitter.on("change", this.onBindingChange_);
}
get label() {
return this.controller_.props.get("label");
}
set label(label) {
this.controller_.props.set("label", label);
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev.event);
});
return this;
}
refresh() {
this.controller_.binding.read();
}
onBindingChange_(ev) {
const value = ev.sender.target.read();
this.emitter_.emit("change", {
event: new TpChangeEvent(this, forceCast(value), this.controller_.binding.target.presetKey, ev.options.last)
});
}
}
class InputBindingController extends LabelController {
constructor(doc, config) {
super(doc, config);
this.binding = config.binding;
}
}
class MonitorBindingApi extends BladeApi {
constructor(controller) {
super(controller);
this.onBindingUpdate_ = this.onBindingUpdate_.bind(this);
this.emitter_ = new Emitter();
this.controller_.binding.emitter.on("update", this.onBindingUpdate_);
}
get label() {
return this.controller_.props.get("label");
}
set label(label) {
this.controller_.props.set("label", label);
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev.event);
});
return this;
}
refresh() {
this.controller_.binding.read();
}
onBindingUpdate_(ev) {
const value = ev.sender.target.read();
this.emitter_.emit("update", {
event: new TpUpdateEvent(this, forceCast(value), this.controller_.binding.target.presetKey)
});
}
}
class MonitorBindingController extends LabelController {
constructor(doc, config) {
super(doc, config);
this.binding = config.binding;
this.viewProps.bindDisabled(this.binding.ticker);
this.viewProps.handleDispose(() => {
this.binding.dispose();
});
}
}
function findSubBladeApiSet(api) {
if (api instanceof RackApi) {
return api["apiSet_"];
}
if (api instanceof RackLikeApi) {
return api["rackApi_"]["apiSet_"];
}
return null;
}
function getApiByController(apiSet, controller) {
const api = apiSet.find((api) => api.controller_ === controller);
if (!api) {
throw TpError.shouldNeverHappen();
}
return api;
}
function createBindingTarget(obj, key, opt_id) {
if (!BindingTarget.isBindable(obj)) {
throw TpError.notBindable();
}
return new BindingTarget(obj, key, opt_id);
}
class RackApi extends BladeApi {
constructor(controller, pool) {
super(controller);
this.onRackAdd_ = this.onRackAdd_.bind(this);
this.onRackRemove_ = this.onRackRemove_.bind(this);
this.onRackInputChange_ = this.onRackInputChange_.bind(this);
this.onRackMonitorUpdate_ = this.onRackMonitorUpdate_.bind(this);
this.emitter_ = new Emitter();
this.apiSet_ = new NestedOrderedSet(findSubBladeApiSet);
this.pool_ = pool;
const rack = this.controller_.rack;
rack.emitter.on("add", this.onRackAdd_);
rack.emitter.on("remove", this.onRackRemove_);
rack.emitter.on("inputchange", this.onRackInputChange_);
rack.emitter.on("monitorupdate", this.onRackMonitorUpdate_);
rack.children.forEach((bc) => {
this.setUpApi_(bc);
});
}
get children() {
return this.controller_.rack.children.map((bc) => getApiByController(this.apiSet_, bc));
}
addInput(object, key, opt_params) {
const params = opt_params !== null && opt_params !== void 0 ? opt_params : {};
const doc = this.controller_.view.element.ownerDocument;
const bc = this.pool_.createInput(doc, createBindingTarget(object, key, params.presetKey), params);
const api = new InputBindingApi(bc);
return this.add(api, params.index);
}
addMonitor(object, key, opt_params) {
const params = opt_params !== null && opt_params !== void 0 ? opt_params : {};
const doc = this.controller_.view.element.ownerDocument;
const bc = this.pool_.createMonitor(doc, createBindingTarget(object, key), params);
const api = new MonitorBindingApi(bc);
return forceCast(this.add(api, params.index));
}
addFolder(params) {
return addFolderAsBlade(this, params);
}
addButton(params) {
return addButtonAsBlade(this, params);
}
addSeparator(opt_params) {
return addSeparatorAsBlade(this, opt_params);
}
addTab(params) {
return addTabAsBlade(this, params);
}
add(api, opt_index) {
this.controller_.rack.add(api.controller_, opt_index);
const gapi = this.apiSet_.find((a) => a.controller_ === api.controller_);
if (gapi) {
this.apiSet_.remove(gapi);
}
this.apiSet_.add(api);
return api;
}
remove(api) {
this.controller_.rack.remove(api.controller_);
}
addBlade(params) {
const doc = this.controller_.view.element.ownerDocument;
const bc = this.pool_.createBlade(doc, params);
const api = this.pool_.createBladeApi(bc);
return this.add(api, params.index);
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev.event);
});
return this;
}
setUpApi_(bc) {
const api = this.apiSet_.find((api) => api.controller_ === bc);
if (!api) {
this.apiSet_.add(this.pool_.createBladeApi(bc));
}
}
onRackAdd_(ev) {
this.setUpApi_(ev.bladeController);
}
onRackRemove_(ev) {
if (ev.isRoot) {
const api = getApiByController(this.apiSet_, ev.bladeController);
this.apiSet_.remove(api);
}
}
onRackInputChange_(ev) {
const bc = ev.bladeController;
if (bc instanceof InputBindingController) {
const api = getApiByController(this.apiSet_, bc);
const binding = bc.binding;
this.emitter_.emit("change", {
event: new TpChangeEvent(api, forceCast(binding.target.read()), binding.target.presetKey, ev.options.last)
});
}
else if (bc instanceof ValueBladeController) {
const api = getApiByController(this.apiSet_, bc);
this.emitter_.emit("change", {
event: new TpChangeEvent(api, bc.value.rawValue, undefined, ev.options.last)
});
}
}
onRackMonitorUpdate_(ev) {
if (!(ev.bladeController instanceof MonitorBindingController)) {
throw TpError.shouldNeverHappen();
}
const api = getApiByController(this.apiSet_, ev.bladeController);
const binding = ev.bladeController.binding;
this.emitter_.emit("update", {
event: new TpUpdateEvent(api, forceCast(binding.target.read()), binding.target.presetKey)
});
}
}
class FolderApi extends RackLikeApi {
constructor(controller, pool) {
super(controller, new RackApi(controller.rackController, pool));
this.emitter_ = new Emitter();
this.controller_.foldable
.value("expanded")
.emitter.on("change", (ev) => {
this.emitter_.emit("fold", {
event: new TpFoldEvent(this, ev.sender.rawValue)
});
});
this.rackApi_.on("change", (ev) => {
this.emitter_.emit("change", {
event: ev
});
});
this.rackApi_.on("update", (ev) => {
this.emitter_.emit("update", {
event: ev
});
});
}
get expanded() {
return this.controller_.foldable.get("expanded");
}
set expanded(expanded) {
this.controller_.foldable.set("expanded", expanded);
}
get title() {
return this.controller_.props.get("title");
}
set title(title) {
this.controller_.props.set("title", title);
}
get children() {
return this.rackApi_.children;
}
addInput(object, key, opt_params) {
return this.rackApi_.addInput(object, key, opt_params);
}
addMonitor(object, key, opt_params) {
return this.rackApi_.addMonitor(object, key, opt_params);
}
addFolder(params) {
return this.rackApi_.addFolder(params);
}
addButton(params) {
return this.rackApi_.addButton(params);
}
addSeparator(opt_params) {
return this.rackApi_.addSeparator(opt_params);
}
addTab(params) {
return this.rackApi_.addTab(params);
}
add(api, opt_index) {
return this.rackApi_.add(api, opt_index);
}
remove(api) {
this.rackApi_.remove(api);
}
addBlade(params) {
return this.rackApi_.addBlade(params);
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev.event);
});
return this;
}
}
class RackLikeController extends BladeController {
constructor(config) {
super({
blade: config.blade,
view: config.view,
viewProps: config.rackController.viewProps
});
this.rackController = config.rackController;
}
}
class PlainView {
constructor(doc, config) {
const className = ClassName(config.viewName);
this.element = doc.createElement("div");
this.element.classList.add(className());
config.viewProps.bindClassModifiers(this.element);
}
}
function findInputBindingController(bcs, b) {
for (let i = 0; i < bcs.length; i++) {
const bc = bcs[i];
if (bc instanceof InputBindingController && bc.binding === b) {
return bc;
}
}
return null;
}
function findMonitorBindingController(bcs, b) {
for (let i = 0; i < bcs.length; i++) {
const bc = bcs[i];
if (bc instanceof MonitorBindingController && bc.binding === b) {
return bc;
}
}
return null;
}
function findValueBladeController(bcs, v) {
for (let i = 0; i < bcs.length; i++) {
const bc = bcs[i];
if (bc instanceof ValueBladeController && bc.value === v) {
return bc;
}
}
return null;
}
function findSubRack(bc) {
if (bc instanceof RackController) {
return bc.rack;
}
if (bc instanceof RackLikeController) {
return bc.rackController.rack;
}
return null;
}
function findSubBladeControllerSet(bc) {
const rack = findSubRack(bc);
return rack ? rack["bcSet_"] : null;
}
class BladeRack {
constructor(blade) {
var _a;
this.onBladePositionsChange_ = this.onBladePositionsChange_.bind(this);
this.onSetAdd_ = this.onSetAdd_.bind(this);
this.onSetRemove_ = this.onSetRemove_.bind(this);
this.onChildDispose_ = this.onChildDispose_.bind(this);
this.onChildPositionsChange_ = this.onChildPositionsChange_.bind(this);
this.onChildInputChange_ = this.onChildInputChange_.bind(this);
this.onChildMonitorUpdate_ = this.onChildMonitorUpdate_.bind(this);
this.onChildValueChange_ = this.onChildValueChange_.bind(this);
this.onChildViewPropsChange_ = this.onChildViewPropsChange_.bind(this);
this.onDescendantLayout_ = this.onDescendantLayout_.bind(this);
this.onDescendantInputChange_ = this.onDescendantInputChange_.bind(this);
this.onDescendantMonitorUpdate_ =
this.onDescendantMonitorUpdate_.bind(this);
this.emitter = new Emitter();
this.blade_ = blade !== null && blade !== void 0 ? blade : null;
(_a = this.blade_) === null || _a === void 0
? void 0
: _a
.value("positions")
.emitter.on("change", this.onBladePositionsChange_);
this.bcSet_ = new NestedOrderedSet(findSubBladeControllerSet);
this.bcSet_.emitter.on("add", this.onSetAdd_);
this.bcSet_.emitter.on("remove", this.onSetRemove_);
}
get children() {
return this.bcSet_.items;
}
add(bc, opt_index) {
if (bc.parent) {
bc.parent.remove(bc);
}
bc["parent_"] = this;
this.bcSet_.add(bc, opt_index);
}
remove(bc) {
bc["parent_"] = null;
this.bcSet_.remove(bc);
}
find(controllerClass) {
return forceCast(this.bcSet_.allItems().filter((bc) => {
return bc instanceof controllerClass;
}));
}
onSetAdd_(ev) {
this.updatePositions_();
const isRoot = ev.target === ev.root;
this.emitter.emit("add", {
bladeController: ev.item,
index: ev.index,
isRoot: isRoot,
sender: this
});
if (!isRoot) {
return;
}
const bc = ev.item;
bc.viewProps.emitter.on("change", this.onChildViewPropsChange_);
bc.blade
.value("positions")
.emitter.on("change", this.onChildPositionsChange_);
bc.viewProps.handleDispose(this.onChildDispose_);
if (bc instanceof InputBindingController) {
bc.binding.emitter.on("change", this.onChildInputChange_);
}
else if (bc instanceof MonitorBindingController) {
bc.binding.emitter.on("update", this.onChildMonitorUpdate_);
}
else if (bc instanceof ValueBladeController) {
bc.value.emitter.on("change", this.onChildValueChange_);
}
else {
const rack = findSubRack(bc);
if (rack) {
const emitter = rack.emitter;
emitter.on("layout", this.onDescendantLayout_);
emitter.on("inputchange", this.onDescendantInputChange_);
emitter.on("monitorupdate", this.onDescendantMonitorUpdate_);
}
}
}
onSetRemove_(ev) {
this.updatePositions_();
const isRoot = ev.target === ev.root;
this.emitter.emit("remove", {
bladeController: ev.item,
isRoot: isRoot,
sender: this
});
if (!isRoot) {
return;
}
const bc = ev.item;
if (bc instanceof InputBindingController) {
bc.binding.emitter.off("change", this.onChildInputChange_);
}
else if (bc instanceof MonitorBindingController) {
bc.binding.emitter.off("update", this.onChildMonitorUpdate_);
}
else if (bc instanceof ValueBladeController) {
bc.value.emitter.off("change", this.onChildValueChange_);
}
else {
const rack = findSubRack(bc);
if (rack) {
const emitter = rack.emitter;
emitter.off("layout", this.onDescendantLayout_);
emitter.off("inputchange", this.onDescendantInputChange_);
emitter.off("monitorupdate", this.onDescendantMonitorUpdate_);
}
}
}
updatePositions_() {
const visibleItems = this.bcSet_.items.filter((bc) => !bc.viewProps.get("hidden"));
const firstVisibleItem = visibleItems[0];
const lastVisibleItem = visibleItems[visibleItems.length - 1];
this.bcSet_.items.forEach((bc) => {
const ps = [];
if (bc === firstVisibleItem) {
ps.push("first");
if (!this.blade_ ||
this.blade_.get("positions").includes("veryfirst")) {
ps.push("veryfirst");
}
}
if (bc === lastVisibleItem) {
ps.push("last");
if (!this.blade_ ||
this.blade_.get("positions").includes("verylast")) {
ps.push("verylast");
}
}
bc.blade.set("positions", ps);
});
}
onChildPositionsChange_() {
this.updatePositions_();
this.emitter.emit("layout", {
sender: this
});
}
onChildViewPropsChange_(_ev) {
this.updatePositions_();
this.emitter.emit("layout", {
sender: this
});
}
onChildDispose_() {
const disposedUcs = this.bcSet_.items.filter((bc) => {
return bc.viewProps.get("disposed");
});
disposedUcs.forEach((bc) => {
this.bcSet_.remove(bc);
});
}
onChildInputChange_(ev) {
const bc = findInputBindingController(this.find(InputBindingController), ev.sender);
if (!bc) {
throw TpError.shouldNeverHappen();
}
this.emitter.emit("inputchange", {
bladeController: bc,
options: ev.options,
sender: this
});
}
onChildMonitorUpdate_(ev) {
const bc = findMonitorBindingController(this.find(MonitorBindingController), ev.sender);
if (!bc) {
throw TpError.shouldNeverHappen();
}
this.emitter.emit("monitorupdate", {
bladeController: bc,
sender: this
});
}
onChildValueChange_(ev) {
const bc = findValueBladeController(this.find(ValueBladeController), ev.sender);
if (!bc) {
throw TpError.shouldNeverHappen();
}
this.emitter.emit("inputchange", {
bladeController: bc,
options: ev.options,
sender: this
});
}
onDescendantLayout_(_) {
this.updatePositions_();
this.emitter.emit("layout", {
sender: this
});
}
onDescendantInputChange_(ev) {
this.emitter.emit("inputchange", {
bladeController: ev.bladeController,
options: ev.options,
sender: this
});
}
onDescendantMonitorUpdate_(ev) {
this.emitter.emit("monitorupdate", {
bladeController: ev.bladeController,
sender: this
});
}
onBladePositionsChange_() {
this.updatePositions_();
}
}
class RackController extends BladeController {
constructor(doc, config) {
super(Object.assign(Object.assign({}, config), {
view: new PlainView(doc, {
viewName: "brk",
viewProps: config.viewProps
})
}));
this.onRackAdd_ = this.onRackAdd_.bind(this);
this.onRackRemove_ = this.onRackRemove_.bind(this);
const rack = new BladeRack(config.root ? undefined : config.blade);
rack.emitter.on("add", this.onRackAdd_);
rack.emitter.on("remove", this.onRackRemove_);
this.rack = rack;
this.viewProps.handleDispose(() => {
for (let i = this.rack.children.length - 1; i >= 0; i--) {
const bc = this.rack.children[i];
bc.viewProps.set("disposed", true);
}
});
}
onRackAdd_(ev) {
if (!ev.isRoot) {
return;
}
insertElementAt(this.view.element, ev.bladeController.view.element, ev.index);
}
onRackRemove_(ev) {
if (!ev.isRoot) {
return;
}
removeElement(ev.bladeController.view.element);
}
}
const bladeContainerClassName = ClassName("cnt");
class FolderView {
constructor(doc, config) {
var _a;
this.className_ = ClassName((_a = config.viewName) !== null && _a !== void 0 ? _a : "fld");
this.element = doc.createElement("div");
this.element.classList.add(this.className_(), bladeContainerClassName());
config.viewProps.bindClassModifiers(this.element);
this.foldable_ = config.foldable;
this.foldable_.bindExpandedClass(this.element, this.className_(undefined, "expanded"));
bindValueMap(this.foldable_, "completed", valueToClassName(this.element, this.className_(undefined, "cpl")));
const buttonElem = doc.createElement("button");
buttonElem.classList.add(this.className_("b"));
bindValueMap(config.props, "title", (title) => {
if (isEmpty(title)) {
this.element.classList.add(this.className_(undefined, "not"));
}
else {
this.element.classList.remove(this.className_(undefined, "not"));
}
});
config.viewProps.bindDisabled(buttonElem);
this.element.appendChild(buttonElem);
this.buttonElement = buttonElem;
const titleElem = doc.createElement("div");
titleElem.classList.add(this.className_("t"));
bindValueToTextContent(config.props.value("title"), titleElem);
this.buttonElement.appendChild(titleElem);
this.titleElement = titleElem;
const markElem = doc.createElement("div");
markElem.classList.add(this.className_("m"));
this.buttonElement.appendChild(markElem);
const containerElem = config.conta