UNPKG

tweakpane4-image-list-plugin

Version:

Image input plugin for Tweakpane

1,697 lines (1,649 loc) 241 kB
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