tweakpane4-image-list-plugin
Version:
Image input plugin for Tweakpane
1,697 lines (1,649 loc) • 241 kB
JavaScript
function forceCast(v) {
return v;
}
function isEmpty(value) {
return value === null || value === undefined;
}
function isObject$1(value) {
return value !== null && typeof value === 'object';
}
function isRecord(value) {
return value !== null && typeof value === 'object';
}
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;
}
function deepMerge(r1, r2) {
const keys = Array.from(new Set([...Object.keys(r1), ...Object.keys(r2)]));
return keys.reduce((result, key) => {
const v1 = r1[key];
const v2 = r2[key];
return isRecord(v1) && isRecord(v2)
? Object.assign(Object.assign({}, result), { [key]: deepMerge(v1, v2) }) : Object.assign(Object.assign({}, result), { [key]: key in r2 ? v2 : v1 });
}, {});
}
function isBinding(value) {
if (!isObject$1(value)) {
return false;
}
return 'target' in value;
}
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`,
notcompatible: (context) => `Not compatible with plugin '${context.id}'`,
propertynotfound: (context) => `Property '${context.name}' not found`,
shouldneverhappen: () => 'This error should never happen',
};
class TpError {
static alreadyDisposed() {
return new TpError({ type: 'alreadydisposed' });
}
static notBindable() {
return new TpError({
type: 'notbindable',
});
}
static notCompatible(bundleId, id) {
return new TpError({
type: 'notcompatible',
context: {
id: `${bundleId}.${id}`,
},
});
}
static propertyNotFound(name) {
return new TpError({
type: 'propertynotfound',
context: {
name: name,
},
});
}
static shouldNeverHappen() {
return new TpError({ type: 'shouldneverhappen' });
}
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;
}
toString() {
return this.message;
}
}
class BindingTarget {
constructor(obj, key) {
this.obj_ = obj;
this.key = key;
}
static isBindable(obj) {
if (obj === null) {
return false;
}
if (typeof obj !== 'object' && typeof obj !== 'function') {
return false;
}
return true;
}
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 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);
});
}
}
class ComplexValue {
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 prevValue = this.rawValue_;
const changed = !this.equals_(prevValue, constrainedValue);
if (!changed && !opts.forceEmit) {
return;
}
this.emitter.emit('beforechange', {
sender: this,
});
this.rawValue_ = constrainedValue;
this.emitter.emit('change', {
options: opts,
previousRawValue: prevValue,
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,
};
const prevValue = this.value_;
if (prevValue === value && !opts.forceEmit) {
return;
}
this.emitter.emit('beforechange', {
sender: this,
});
this.value_ = value;
this.emitter.emit('change', {
options: opts,
previousRawValue: prevValue,
rawValue: this.value_,
sender: this,
});
}
}
class ReadonlyPrimitiveValue {
constructor(value) {
this.emitter = new Emitter();
this.onValueBeforeChange_ = this.onValueBeforeChange_.bind(this);
this.onValueChange_ = this.onValueChange_.bind(this);
this.value_ = value;
this.value_.emitter.on('beforechange', this.onValueBeforeChange_);
this.value_.emitter.on('change', this.onValueChange_);
}
get rawValue() {
return this.value_.rawValue;
}
onValueBeforeChange_(ev) {
this.emitter.emit('beforechange', Object.assign(Object.assign({}, ev), { sender: this }));
}
onValueChange_(ev) {
this.emitter.emit('change', Object.assign(Object.assign({}, ev), { 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 ComplexValue(initialValue, config);
}
function createReadonlyValue(value) {
return [
new ReadonlyPrimitiveValue(value),
(rawValue, options) => {
value.setRawValue(rawValue, options);
},
];
}
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];
}
}
class DefiniteRangeConstraint {
constructor(config) {
this.values = ValueMap.fromObject({
max: config.max,
min: config.min,
});
}
constrain(value) {
const max = this.values.get('max');
const min = this.values.get('min');
return Math.min(Math.max(value, min), max);
}
}
class RangeConstraint {
constructor(config) {
this.values = ValueMap.fromObject({
max: config.max,
min: config.min,
});
}
constrain(value) {
const max = this.values.get('max');
const min = this.values.get('min');
let result = value;
if (!isEmpty(min)) {
result = Math.max(result, min);
}
if (!isEmpty(max)) {
result = Math.min(result, max);
}
return result;
}
}
class StepConstraint {
constructor(step, origin = 0) {
this.step = step;
this.origin = origin;
}
constrain(value) {
const o = this.origin % this.step;
const r = Math.round((value - o) / this.step);
return o + r * this.step;
}
}
class NumberLiteralNode {
constructor(text) {
this.text = text;
}
evaluate() {
return Number(this.text);
}
toString() {
return this.text;
}
}
const BINARY_OPERATION_MAP = {
'**': (v1, v2) => Math.pow(v1, v2),
'*': (v1, v2) => v1 * v2,
'/': (v1, v2) => v1 / v2,
'%': (v1, v2) => v1 % v2,
'+': (v1, v2) => v1 + v2,
'-': (v1, v2) => v1 - v2,
'<<': (v1, v2) => v1 << v2,
'>>': (v1, v2) => v1 >> v2,
'>>>': (v1, v2) => v1 >>> v2,
'&': (v1, v2) => v1 & v2,
'^': (v1, v2) => v1 ^ v2,
'|': (v1, v2) => v1 | v2,
};
class BinaryOperationNode {
constructor(operator, left, right) {
this.left = left;
this.operator = operator;
this.right = right;
}
evaluate() {
const op = BINARY_OPERATION_MAP[this.operator];
if (!op) {
throw new Error(`unexpected binary operator: '${this.operator}`);
}
return op(this.left.evaluate(), this.right.evaluate());
}
toString() {
return [
'b(',
this.left.toString(),
this.operator,
this.right.toString(),
')',
].join(' ');
}
}
const UNARY_OPERATION_MAP = {
'+': (v) => v,
'-': (v) => -v,
'~': (v) => ~v,
};
class UnaryOperationNode {
constructor(operator, expr) {
this.operator = operator;
this.expression = expr;
}
evaluate() {
const op = UNARY_OPERATION_MAP[this.operator];
if (!op) {
throw new Error(`unexpected unary operator: '${this.operator}`);
}
return op(this.expression.evaluate());
}
toString() {
return ['u(', this.operator, this.expression.toString(), ')'].join(' ');
}
}
function combineReader(parsers) {
return (text, cursor) => {
for (let i = 0; i < parsers.length; i++) {
const result = parsers[i](text, cursor);
if (result !== '') {
return result;
}
}
return '';
};
}
function readWhitespace(text, cursor) {
var _a;
const m = text.substr(cursor).match(/^\s+/);
return (_a = (m && m[0])) !== null && _a !== void 0 ? _a : '';
}
function readNonZeroDigit(text, cursor) {
const ch = text.substr(cursor, 1);
return ch.match(/^[1-9]$/) ? ch : '';
}
function readDecimalDigits(text, cursor) {
var _a;
const m = text.substr(cursor).match(/^[0-9]+/);
return (_a = (m && m[0])) !== null && _a !== void 0 ? _a : '';
}
function readSignedInteger(text, cursor) {
const ds = readDecimalDigits(text, cursor);
if (ds !== '') {
return ds;
}
const sign = text.substr(cursor, 1);
cursor += 1;
if (sign !== '-' && sign !== '+') {
return '';
}
const sds = readDecimalDigits(text, cursor);
if (sds === '') {
return '';
}
return sign + sds;
}
function readExponentPart(text, cursor) {
const e = text.substr(cursor, 1);
cursor += 1;
if (e.toLowerCase() !== 'e') {
return '';
}
const si = readSignedInteger(text, cursor);
if (si === '') {
return '';
}
return e + si;
}
function readDecimalIntegerLiteral(text, cursor) {
const ch = text.substr(cursor, 1);
if (ch === '0') {
return ch;
}
const nzd = readNonZeroDigit(text, cursor);
cursor += nzd.length;
if (nzd === '') {
return '';
}
return nzd + readDecimalDigits(text, cursor);
}
function readDecimalLiteral1(text, cursor) {
const dil = readDecimalIntegerLiteral(text, cursor);
cursor += dil.length;
if (dil === '') {
return '';
}
const dot = text.substr(cursor, 1);
cursor += dot.length;
if (dot !== '.') {
return '';
}
const dds = readDecimalDigits(text, cursor);
cursor += dds.length;
return dil + dot + dds + readExponentPart(text, cursor);
}
function readDecimalLiteral2(text, cursor) {
const dot = text.substr(cursor, 1);
cursor += dot.length;
if (dot !== '.') {
return '';
}
const dds = readDecimalDigits(text, cursor);
cursor += dds.length;
if (dds === '') {
return '';
}
return dot + dds + readExponentPart(text, cursor);
}
function readDecimalLiteral3(text, cursor) {
const dil = readDecimalIntegerLiteral(text, cursor);
cursor += dil.length;
if (dil === '') {
return '';
}
return dil + readExponentPart(text, cursor);
}
const readDecimalLiteral = combineReader([
readDecimalLiteral1,
readDecimalLiteral2,
readDecimalLiteral3,
]);
function parseBinaryDigits(text, cursor) {
var _a;
const m = text.substr(cursor).match(/^[01]+/);
return (_a = (m && m[0])) !== null && _a !== void 0 ? _a : '';
}
function readBinaryIntegerLiteral(text, cursor) {
const prefix = text.substr(cursor, 2);
cursor += prefix.length;
if (prefix.toLowerCase() !== '0b') {
return '';
}
const bds = parseBinaryDigits(text, cursor);
if (bds === '') {
return '';
}
return prefix + bds;
}
function readOctalDigits(text, cursor) {
var _a;
const m = text.substr(cursor).match(/^[0-7]+/);
return (_a = (m && m[0])) !== null && _a !== void 0 ? _a : '';
}
function readOctalIntegerLiteral(text, cursor) {
const prefix = text.substr(cursor, 2);
cursor += prefix.length;
if (prefix.toLowerCase() !== '0o') {
return '';
}
const ods = readOctalDigits(text, cursor);
if (ods === '') {
return '';
}
return prefix + ods;
}
function readHexDigits(text, cursor) {
var _a;
const m = text.substr(cursor).match(/^[0-9a-f]+/i);
return (_a = (m && m[0])) !== null && _a !== void 0 ? _a : '';
}
function readHexIntegerLiteral(text, cursor) {
const prefix = text.substr(cursor, 2);
cursor += prefix.length;
if (prefix.toLowerCase() !== '0x') {
return '';
}
const hds = readHexDigits(text, cursor);
if (hds === '') {
return '';
}
return prefix + hds;
}
const readNonDecimalIntegerLiteral = combineReader([
readBinaryIntegerLiteral,
readOctalIntegerLiteral,
readHexIntegerLiteral,
]);
const readNumericLiteral = combineReader([
readNonDecimalIntegerLiteral,
readDecimalLiteral,
]);
function parseLiteral(text, cursor) {
const num = readNumericLiteral(text, cursor);
cursor += num.length;
if (num === '') {
return null;
}
return {
evaluable: new NumberLiteralNode(num),
cursor: cursor,
};
}
function parseParenthesizedExpression(text, cursor) {
const op = text.substr(cursor, 1);
cursor += op.length;
if (op !== '(') {
return null;
}
const expr = parseExpression(text, cursor);
if (!expr) {
return null;
}
cursor = expr.cursor;
cursor += readWhitespace(text, cursor).length;
const cl = text.substr(cursor, 1);
cursor += cl.length;
if (cl !== ')') {
return null;
}
return {
evaluable: expr.evaluable,
cursor: cursor,
};
}
function parsePrimaryExpression(text, cursor) {
var _a;
return ((_a = parseLiteral(text, cursor)) !== null && _a !== void 0 ? _a : parseParenthesizedExpression(text, cursor));
}
function parseUnaryExpression(text, cursor) {
const expr = parsePrimaryExpression(text, cursor);
if (expr) {
return expr;
}
const op = text.substr(cursor, 1);
cursor += op.length;
if (op !== '+' && op !== '-' && op !== '~') {
return null;
}
const num = parseUnaryExpression(text, cursor);
if (!num) {
return null;
}
cursor = num.cursor;
return {
cursor: cursor,
evaluable: new UnaryOperationNode(op, num.evaluable),
};
}
function readBinaryOperator(ops, text, cursor) {
cursor += readWhitespace(text, cursor).length;
const op = ops.filter((op) => text.startsWith(op, cursor))[0];
if (!op) {
return null;
}
cursor += op.length;
cursor += readWhitespace(text, cursor).length;
return {
cursor: cursor,
operator: op,
};
}
function createBinaryOperationExpressionParser(exprParser, ops) {
return (text, cursor) => {
const firstExpr = exprParser(text, cursor);
if (!firstExpr) {
return null;
}
cursor = firstExpr.cursor;
let expr = firstExpr.evaluable;
for (;;) {
const op = readBinaryOperator(ops, text, cursor);
if (!op) {
break;
}
cursor = op.cursor;
const nextExpr = exprParser(text, cursor);
if (!nextExpr) {
return null;
}
cursor = nextExpr.cursor;
expr = new BinaryOperationNode(op.operator, expr, nextExpr.evaluable);
}
return expr
? {
cursor: cursor,
evaluable: expr,
}
: null;
};
}
const parseBinaryOperationExpression = [
['**'],
['*', '/', '%'],
['+', '-'],
['<<', '>>>', '>>'],
['&'],
['^'],
['|'],
].reduce((parser, ops) => {
return createBinaryOperationExpressionParser(parser, ops);
}, parseUnaryExpression);
function parseExpression(text, cursor) {
cursor += readWhitespace(text, cursor).length;
return parseBinaryOperationExpression(text, cursor);
}
function parseEcmaNumberExpression(text) {
const expr = parseExpression(text, 0);
if (!expr) {
return null;
}
const cursor = expr.cursor + readWhitespace(text, expr.cursor).length;
if (cursor !== text.length) {
return null;
}
return expr.evaluable;
}
function parseNumber(text) {
var _a;
const r = parseEcmaNumberExpression(text);
return (_a = r === null || r === void 0 ? void 0 : r.evaluate()) !== null && _a !== void 0 ? _a : null;
}
function numberFromUnknown(value) {
if (typeof value === 'number') {
return value;
}
if (typeof value === 'string') {
const pv = parseNumber(value);
if (!isEmpty(pv)) {
return pv;
}
}
return 0;
}
function createNumberFormatter(digits) {
return (value) => {
return value.toFixed(Math.max(Math.min(digits, 20), 0));
};
}
function mapRange(value, start1, end1, start2, end2) {
const p = (value - start1) / (end1 - start1);
return start2 + p * (end2 - start2);
}
function getDecimalDigits(value) {
const text = String(value.toFixed(10));
const frac = text.split('.')[1];
return frac.replace(/0+$/, '').length;
}
function constrainRange(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function loopRange(value, max) {
return ((value % max) + max) % max;
}
function getSuitableDecimalDigits(params, rawValue) {
return !isEmpty(params.step)
? getDecimalDigits(params.step)
: Math.max(getDecimalDigits(rawValue), 2);
}
function getSuitableKeyScale(params) {
var _a;
return (_a = params.step) !== null && _a !== void 0 ? _a : 1;
}
function getSuitablePointerScale(params, rawValue) {
var _a;
const base = Math.abs((_a = params.step) !== null && _a !== void 0 ? _a : rawValue);
return base === 0 ? 0.1 : Math.pow(10, Math.floor(Math.log10(base)) - 1);
}
function createStepConstraint(params, initialValue) {
if (!isEmpty(params.step)) {
return new StepConstraint(params.step, initialValue);
}
return null;
}
function createRangeConstraint(params) {
if (!isEmpty(params.max) && !isEmpty(params.min)) {
return new DefiniteRangeConstraint({
max: params.max,
min: params.min,
});
}
if (!isEmpty(params.max) || !isEmpty(params.min)) {
return new RangeConstraint({
max: params.max,
min: params.min,
});
}
return null;
}
function createNumberTextPropsObject(params, initialValue) {
var _a, _b, _c;
return {
formatter: (_a = params.format) !== null && _a !== void 0 ? _a : createNumberFormatter(getSuitableDecimalDigits(params, initialValue)),
keyScale: (_b = params.keyScale) !== null && _b !== void 0 ? _b : getSuitableKeyScale(params),
pointerScale: (_c = params.pointerScale) !== null && _c !== void 0 ? _c : getSuitablePointerScale(params, initialValue),
};
}
function createNumberTextInputParamsParser(p) {
return {
format: p.optional.function,
keyScale: p.optional.number,
max: p.optional.number,
min: p.optional.number,
pointerScale: p.optional.number,
step: p.optional.number,
};
}
function createPointAxis(config) {
return {
constraint: config.constraint,
textProps: ValueMap.fromObject(createNumberTextPropsObject(config.params, config.initialValue)),
};
}
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);
}
importState(state) {
return this.controller.importState(state);
}
exportState() {
return this.controller.exportState();
}
}
class TpEvent {
constructor(target) {
this.target = target;
}
}
class TpChangeEvent extends TpEvent {
constructor(target, value, last) {
super(target);
this.value = value;
this.last = last !== null && last !== void 0 ? last : true;
}
}
class TpFoldEvent extends TpEvent {
constructor(target, expanded) {
super(target);
this.expanded = expanded;
}
}
class TpTabSelectEvent extends TpEvent {
constructor(target, index) {
super(target);
this.index = index;
}
}
class BindingApi extends BladeApi {
constructor(controller) {
super(controller);
this.onValueChange_ = this.onValueChange_.bind(this);
this.emitter_ = new Emitter();
this.controller.value.emitter.on('change', this.onValueChange_);
}
get label() {
return this.controller.labelController.props.get('label');
}
set label(label) {
this.controller.labelController.props.set('label', label);
}
get key() {
return this.controller.value.binding.target.key;
}
get tag() {
return this.controller.tag;
}
set tag(tag) {
this.controller.tag = tag;
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev);
});
return this;
}
refresh() {
this.controller.value.fetch();
}
onValueChange_(ev) {
const value = this.controller.value;
this.emitter_.emit('change', new TpChangeEvent(this, forceCast(value.binding.target.read()), ev.options.last));
}
}
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 createMicroParserBuilder(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 createMicroParserBuilders(optional) {
return {
custom: (parse) => createMicroParserBuilder(parse)(optional),
boolean: createMicroParserBuilder((v) => typeof v === 'boolean' ? v : undefined)(optional),
number: createMicroParserBuilder((v) => typeof v === 'number' ? v : undefined)(optional),
string: createMicroParserBuilder((v) => typeof v === 'string' ? v : undefined)(optional),
function: createMicroParserBuilder((v) =>
typeof v === 'function' ? v : undefined)(optional),
constant: (value) => createMicroParserBuilder((v) => (v === value ? value : undefined))(optional),
raw: createMicroParserBuilder((v) => v)(optional),
object: (keyToParserMap) => createMicroParserBuilder((v) => {
if (!isObject(v)) {
return undefined;
}
return parseObject(v, keyToParserMap);
})(optional),
array: (itemParser) => createMicroParserBuilder((v) => {
if (!Array.isArray(v)) {
return undefined;
}
return parseArray(v, itemParser);
})(optional),
};
}
const MicroParsers = {
optional: createMicroParserBuilders(true),
required: createMicroParserBuilders(false),
};
function parseRecord(value, keyToParserMap) {
const map = keyToParserMap(MicroParsers);
const result = MicroParsers.required.object(map)(value);
return result.succeeded ? result.value : undefined;
}
function importBladeState(state, superImport, parser, callback) {
if (superImport && !superImport(state)) {
return false;
}
const result = parseRecord(state, parser);
return result ? callback(result) : false;
}
function exportBladeState(superExport, thisState) {
var _a;
return deepMerge((_a = superExport === null || superExport === void 0 ? void 0 : superExport()) !== null && _a !== void 0 ? _a : {}, thisState);
}
function isValueBladeController(bc) {
return 'value' in bc;
}
function isBindingValue(v) {
if (!isObject$1(v) || !('binding' in v)) {
return false;
}
const b = v.binding;
return isBinding(b);
}
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 getCanvasContext(canvasElement) {
const win = canvasElement.ownerDocument.defaultView;
if (!win) {
return null;
}
const isBrowser = 'document' in win;
return isBrowser
? canvasElement.getContext('2d', {
willReadFrequently: true,
})
: 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;
}
function bindValue(value, applyValue) {
value.emitter.on('change', (ev) => {
applyValue(ev.rawValue);
});
applyValue(value.rawValue);
}
function bindValueMap(valueMap, key, applyValue) {
bindValue(valueMap.value(key), applyValue);
}
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;
}
const cn$q = 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(cn$q());
config.viewProps.bindClassModifiers(this.element);
const labelElem = doc.createElement('div');
labelElem.classList.add(cn$q('l'));
bindValueMap(config.props, 'label', (value) => {
if (isEmpty(value)) {
this.element.classList.add(cn$q(undefined, 'nol'));
}
else {
this.element.classList.remove(cn$q(undefined, 'nol'));
removeChildNodes(labelElem);
labelElem.appendChild(createLabelNode(doc, value));
}
});
this.element.appendChild(labelElem);
this.labelElement = labelElem;
const valueElem = doc.createElement('div');
valueElem.classList.add(cn$q('v'));
this.element.appendChild(valueElem);
this.valueElement = valueElem;
}
}
class LabelController {
constructor(doc, config) {
this.props = config.props;
this.valueController = config.valueController;
this.viewProps = config.valueController.viewProps;
this.view = new LabelView(doc, {
props: config.props,
viewProps: this.viewProps,
});
this.view.valueElement.appendChild(this.valueController.view.element);
}
importProps(state) {
return importBladeState(state, null, (p) => ({
label: p.optional.string,
}), (result) => {
this.props.set('label', result.label);
return true;
});
}
exportProps() {
return exportBladeState(null, {
label: this.props.get('label'),
});
}
}
function getAllBladePositions() {
return ['veryfirst', 'first', 'last', 'verylast'];
}
const cn$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(cn$p(undefined, POS_TO_CLASS_NAME_MAP[pos]));
});
this.blade.get('positions').forEach((pos) => {
elem.classList.add(cn$p(undefined, POS_TO_CLASS_NAME_MAP[pos]));
});
});
this.viewProps.handleDispose(() => {
removeElement(elem);
});
}
get parent() {
return this.parent_;
}
set parent(parent) {
this.parent_ = parent;
this.viewProps.set('parent', this.parent_ ? this.parent_.viewProps : null);
}
importState(state) {
return importBladeState(state, null, (p) => ({
disabled: p.required.boolean,
hidden: p.required.boolean,
}), (result) => {
this.viewProps.importState(result);
return true;
});
}
exportState() {
return exportBladeState(null, Object.assign({}, this.viewProps.exportState()));
}
}
class ButtonApi extends BladeApi {
get label() {
return this.controller.labelController.props.get('label');
}
set label(label) {
this.controller.labelController.props.set('label', label);
}
get title() {
var _a;
return (_a = this.controller.buttonController.props.get('title')) !== null && _a !== void 0 ? _a : '';
}
set title(title) {
this.controller.buttonController.props.set('title', title);
}
on(eventName, handler) {
const bh = handler.bind(this);
const emitter = this.controller.buttonController.emitter;
emitter.on(eventName, () => {
bh(new TpEvent(this));
});
return this;
}
}
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 cn$o = ClassName('btn');
class ButtonView {
constructor(doc, config) {
this.element = doc.createElement('div');
this.element.classList.add(cn$o());
config.viewProps.bindClassModifiers(this.element);
const buttonElem = doc.createElement('button');
buttonElem.classList.add(cn$o('b'));
config.viewProps.bindDisabled(buttonElem);
this.element.appendChild(buttonElem);
this.buttonElement = buttonElem;
const titleElem = doc.createElement('div');
titleElem.classList.add(cn$o('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_);
}
importProps(state) {
return importBladeState(state, null, (p) => ({
title: p.optional.string,
}), (result) => {
this.props.set('title', result.title);
return true;
});
}
exportProps() {
return exportBladeState(null, {
title: this.props.get('title'),
});
}
onClick_() {
this.emitter.emit('click', {
sender: this,
});
}
}
class ButtonBladeController extends BladeController {
constructor(doc, config) {
const bc = new ButtonController(doc, {
props: config.buttonProps,
viewProps: config.viewProps,
});
const lc = new LabelController(doc, {
blade: config.blade,
props: config.labelProps,
valueController: bc,
});
super({
blade: config.blade,
view: lc.view,
viewProps: config.viewProps,
});
this.buttonController = bc;
this.labelController = lc;
}
importState(state) {
return importBladeState(state, (s) => super.importState(s) &&
this.buttonController.importProps(s) &&
this.labelController.importProps(s), () => ({}), () => true);
}
exportState() {
return exportBladeState(() => super.exportState(), Object.assign(Object.assign({}, this.buttonController.exportProps()), this.labelController.exportProps()));
}
}
class Semver {
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;
}
}
const VERSION = new Semver('2.0.1');
function createPlugin(plugin) {
return Object.assign({ core: VERSION }, plugin);
}
createPlugin({
id: 'button',
type: 'blade',
accept(params) {
const result = parseRecord(params, (p) => ({
title: p.required.string,
view: p.required.constant('button'),
label: p.optional.string,
}));
return result ? { params: result } : null;
},
controller(args) {
return new ButtonBladeController(args.document, {
blade: args.blade,
buttonProps: ValueMap.fromObject({
title: args.params.title,
}),
labelProps: ValueMap.fromObject({
label: args.params.label,
}),
viewProps: args.viewProps,
});
},
api(args) {
if (args.controller instanceof ButtonBladeController) {
return new ButtonApi(args.controller);
}
return null;
},
});
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 addTabAsBlade(api, params) {
return api.addBlade(Object.assign(Object.assign({}, params), { view: 'tab' }));
}
function isRefreshable(value) {
if (!isObject$1(value)) {
return false;
}
return 'refresh' in value && typeof value.refresh === 'function';
}
function createBindingTarget(obj, key) {
if (!BindingTarget.isBindable(obj)) {
throw TpError.notBindable();
}
return new BindingTarget(obj, key);
}
class RackApi {
constructor(controller, pool) {
this.onRackValueChange_ = this.onRackValueChange_.bind(this);
this.controller_ = controller;
this.emitter_ = new Emitter();
this.pool_ = pool;
const rack = this.controller_.rack;
rack.emitter.on('valuechange', this.onRackValueChange_);
}
get children() {
return this.controller_.rack.children.map((bc) => this.pool_.createApi(bc));
}
addBinding(object, key, opt_params) {
const params = opt_params !== null && opt_params !== void 0 ? opt_params : {};
const doc = this.controller_.element.ownerDocument;
const bc = this.pool_.createBinding(doc, createBindingTarget(object, key), params);
const api = this.pool_.createBindingApi(bc);
return this.add(api, params.index);
}
addFolder(params) {
return addFolderAsBlade(this, params);
}
addButton(params) {
return addButtonAsBlade(this, params);
}
addTab(params) {
return addTabAsBlade(this, params);
}
add(api, opt_index) {
const bc = api.controller;
this.controller_.rack.add(bc, opt_index);
return api;
}
remove(api) {
this.controller_.rack.remove(api.controller);
}
addBlade(params) {
const doc = this.controller_.element.ownerDocument;
const bc = this.pool_.createBlade(doc, params);
const api = this.pool_.createApi(bc);
return this.add(api, params.index);
}
on(eventName, handler) {
const bh = handler.bind(this);
this.emitter_.on(eventName, (ev) => {
bh(ev);
});
return this;
}
refresh() {
this.children.forEach((c) => {
if (isRefreshable(c)) {
c.refresh();
}
});
}
onRackValueChange_(ev) {
const bc = ev.bladeController;
const api = this.pool_.createApi(bc);
const binding = isBindingValue(bc.value) ? bc.value.binding : null;
this.emitter_.emit('change', new TpChangeEvent(api, binding ? binding.target.read() : bc.value.rawValue, ev.options.last));
}
}
class ContainerBladeApi extends BladeApi {
constructor(controller, pool) {
super(controller);
this.rackApi_ = new RackApi(controller.rackController, pool);
}
refresh() {
this.rackApi_.refresh();
}
}
class ContainerBladeController extends BladeController {
constructor(config) {
super({
blade: config.blade,
view: config.view,
viewProps: config.rackController.viewProps,
});
this.rackController = config.rackController;
}
importState(state) {
return importBladeState(state, (s) => super.importState(s), (p) => ({
children: p.required.array(p.required.raw),
}), (result) => {
return this.rackController.rack.children.every((c, index) => {
return c.importState(result.children[index]);
});
});
}
exportState() {
return exportBladeState(() => super.exportState(), {
children: this.rackController.rack.children.map((c) => c.exportState()),
});
}
}
function isContainerBladeController(bc) {
return 'rackController' in bc;
}
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((i) => {
this.cache_.add(i);
});
}
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.allItems().forEach((i) => {
this.cache_.delete(i);
});
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,
});
}
}
function findValueBladeController(bcs, v) {
for (let i = 0; i < bcs.length; i++) {
const bc = bcs[i];
if (isValueBladeController(bc) && bc.value === v) {
return bc;
}
}
return null;
}
function findSubBladeControllerSet(bc) {
return isContainerBladeController(bc)
? bc.rackController.rack['bcSet_']
: null;
}
class Rack {
constructor(config) {
var _a, _b;
this.emitter = new Emitter();
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.onChildValueChange_ = this.onChildValueChange_.bind(this);
this.onChildViewPropsChange_ = this.onChildViewPropsChange_.bind(this);
this.onRackLayout_ = this.onRackLayout_.bind(this);
this.onRackValueChange_ = this.onRackValueChange_.bind(this);
this.blade_ = (_a = config.blade) !== null && _a !== void 0 ? _a : null;
(_b = this.blade_) === null || _b === void 0 ? void 0 : _b.value('positions').emitter.on('change', this.onBladePositionsChange_);
this.viewProps = config.viewProps;
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) {
var _a;
(_a = bc.parent) === null || _a === void 0 ? void 0 : _a.remove(bc);
bc.parent = this;
this.bcSet_.add(bc, opt_index);
}
remove(bc) {
bc.parent = null;
this.bcSet_.remove(bc);
}
find(finder) {
return this.bcSet_.allItems().filter(finder);
}
onSetAdd_(ev) {
this.updatePositions_();
const root = ev.target === ev.root;
this.emitter.emit('add', {
bladeController: ev.item,
index: ev.index,
root: root,
sender: this,
});
if (!root) {
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 (isValueBladeController(bc)) {
bc.value.emitter.on('change', this.onChildValueChange_);
}
else if (isContainerBladeController(bc)) {
const rack = bc.rack