@mapbox/mapbox-gl-style-spec 14.8.0-beta.1 → 14.9.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -138,7 +138,7 @@ class Coercion implements Expression {
138
138
  // created by properties that expect the 'formatted' type.
139
139
  return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
140
140
  } else if (this.type.kind === 'resolvedImage') {
141
- return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
141
+ return ResolvedImage.build(valueToString(this.args[0].evaluate(ctx)));
142
142
  } else if (this.type.kind === 'array') {
143
143
  return this.args.map(arg => { return arg.evaluate(ctx); });
144
144
  } else {
@@ -19,7 +19,7 @@ function coerceValue(type: string, value: any): any {
19
19
  return Formatted.fromString(valueToString(value));
20
20
  }
21
21
  case 'resolvedImage': {
22
- return ResolvedImage.fromString(valueToString(value));
22
+ return ResolvedImage.build(valueToString(value));
23
23
  }
24
24
  }
25
25
  return value;
@@ -1,20 +1,47 @@
1
- import {ResolvedImageType, StringType} from '../types';
1
+ import {ColorType, ResolvedImageType, StringType} from '../types';
2
2
  import ResolvedImage from '../types/resolved_image';
3
+ import {isExpression} from '..';
3
4
 
5
+ import type Color from '../../util/color';
4
6
  import type {Expression, SerializedExpression} from '../expression';
5
7
  import type EvaluationContext from '../evaluation_context';
6
8
  import type ParsingContext from '../parsing_context';
7
9
  import type {Type} from '../types';
8
10
 
11
+ export type ImageParams = Record<string, Expression>;
12
+
13
+ export type ImageOptions = {
14
+ params: ImageParams;
15
+ }
16
+
17
+ function isImageOptions(value: unknown) {
18
+ if (typeof value !== 'string' && !isExpression(value)) {
19
+ return true;
20
+ }
21
+
22
+ return false;
23
+ }
24
+
9
25
  export default class ImageExpression implements Expression {
10
26
  type: Type;
11
27
  inputPrimary: Expression;
28
+ inputPrimaryParams: Record<string, Expression> | undefined;
12
29
  inputSecondary: Expression | null | undefined;
30
+ inputSecondaryParams: Record<string, Expression> | undefined;
13
31
 
14
- constructor(inputPrimary: Expression, inputSecondary?: Expression | null) {
32
+ _imageWarnHistory: Record<string, boolean> = {};
33
+
34
+ constructor(
35
+ inputPrimary: Expression,
36
+ inputSecondary?: Expression | null,
37
+ inputPrimaryParams?: Record<string, Expression>,
38
+ inputSecondaryParams?: Record<string, Expression>
39
+ ) {
15
40
  this.type = ResolvedImageType;
16
41
  this.inputPrimary = inputPrimary;
17
42
  this.inputSecondary = inputSecondary;
43
+ this.inputPrimaryParams = inputPrimaryParams;
44
+ this.inputSecondaryParams = inputSecondaryParams;
18
45
  }
19
46
 
20
47
  static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | null | undefined {
@@ -23,23 +50,126 @@ export default class ImageExpression implements Expression {
23
50
  return context.error(`Expected two or more arguments.`);
24
51
  }
25
52
 
26
- const namePrimary = context.parse(args[1], 1, StringType);
27
- // @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Expression'.
28
- if (!namePrimary) return context.error(`No image name provided.`);
53
+ let nextArgId = 1;
54
+ const imageExpression: Array<{image: Expression, options: Record<string, Expression>}> = [];
55
+
56
+ function tryParseImage() {
57
+ if (nextArgId < args.length) {
58
+ const imageName = context.parse(args[nextArgId], nextArgId++, StringType);
59
+ if (!imageName) {
60
+ context.error(imageExpression.length ? `Secondary image variant is not a string.` : `No image name provided.`);
61
+ return false;
62
+ }
63
+
64
+ imageExpression.push({image: imageName, options: undefined});
65
+ return true;
66
+ }
67
+
68
+ return true;
69
+ }
70
+
71
+ function tryParseOptions() {
72
+ if (nextArgId < args.length) {
73
+ if (!isImageOptions(args[nextArgId])) {
74
+ return true;
75
+ }
76
+
77
+ const params = (args[nextArgId] as ImageOptions).params;
78
+
79
+ const optionsContext = context.concat(nextArgId);
80
+
81
+ if (!params) {
82
+ nextArgId++;
83
+ return true;
84
+ }
85
+
86
+ if (typeof params !== 'object' || params.constructor !== Object) {
87
+ optionsContext.error(`Image options \"params\" should be an object`);
88
+ return false;
89
+ }
90
+
91
+ const parsed = {};
92
+
93
+ const childContext = optionsContext.concat(undefined, 'params');
94
+
95
+ for (const key in params) {
96
+ if (!key) {
97
+ childContext.error(`Image parameter name should be non-empty`);
98
+ return false;
99
+ }
100
+
101
+ const value = childContext.concat(undefined, key).parse(params[key], undefined, ColorType, undefined, {typeAnnotation: 'coerce'});
102
+ if (!value) {
103
+ return false;
104
+ }
105
+
106
+ parsed[key] = value;
107
+ }
108
+
109
+ imageExpression[imageExpression.length - 1].options = parsed;
110
+ nextArgId++;
111
+
112
+ return true;
113
+ }
114
+
115
+ return true;
116
+ }
117
+
118
+ // Parse the primary and secondary image expressions
119
+ for (let i = 0; i < 2; i++) {
120
+ if (!tryParseImage() || !tryParseOptions()) {
121
+ return;
122
+ }
123
+ }
29
124
 
30
- if (args.length === 2) {
31
- return new ImageExpression(namePrimary);
125
+ return new ImageExpression(
126
+ imageExpression[0].image,
127
+ imageExpression[1] ? imageExpression[1].image : undefined,
128
+ imageExpression[0].options,
129
+ imageExpression[1] ? imageExpression[1].options : undefined
130
+ );
131
+ }
132
+
133
+ evaluateParams(ctx: EvaluationContext, params: Record<string, Expression> | undefined): {params: Record<string, Color>} {
134
+ const result: Record<string, Color> = {};
135
+ if (params) {
136
+ for (const key in params) {
137
+ if (params[key]) {
138
+ try {
139
+ const color = params[key].evaluate(ctx);
140
+ const msg = `Ignoring image parameter "${key}" with semi-transparent color ${color.toString()}`;
141
+
142
+ if (color.a !== 1) {
143
+ if (!this._imageWarnHistory[msg]) {
144
+ console.warn(msg);
145
+ this._imageWarnHistory[msg] = true;
146
+ }
147
+ continue;
148
+ }
149
+ result[key] = color;
150
+ } catch (err) {
151
+ continue;
152
+ }
153
+ }
154
+ }
155
+ } else {
156
+ return undefined;
32
157
  }
33
158
 
34
- const nameSecondary = context.parse(args[2], 1, StringType);
35
- // @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Expression'.
36
- if (!nameSecondary) return context.error(`Secondary image variant is not a string.`);
159
+ if (Object.keys(result).length === 0) {
160
+ return undefined;
161
+ }
37
162
 
38
- return new ImageExpression(namePrimary, nameSecondary);
163
+ return {params: result};
39
164
  }
40
165
 
41
166
  evaluate(ctx: EvaluationContext): null | ResolvedImage {
42
- const value = ResolvedImage.fromString(this.inputPrimary.evaluate(ctx), this.inputSecondary ? this.inputSecondary.evaluate(ctx) : undefined);
167
+ const value = ResolvedImage.build(
168
+ this.inputPrimary.evaluate(ctx),
169
+ this.inputSecondary ? this.inputSecondary.evaluate(ctx) : undefined,
170
+ this.inputPrimaryParams ? this.evaluateParams(ctx, this.inputPrimaryParams) : undefined,
171
+ this.inputSecondaryParams ? this.evaluateParams(ctx, this.inputSecondaryParams) : undefined
172
+ );
43
173
  if (value && ctx.availableImages) {
44
174
  value.available = ctx.availableImages.indexOf(value.namePrimary) > -1;
45
175
  // If there's a secondary variant, only mark it available if both are present
@@ -53,8 +183,22 @@ export default class ImageExpression implements Expression {
53
183
 
54
184
  eachChild(fn: (_: Expression) => void) {
55
185
  fn(this.inputPrimary);
186
+ if (this.inputPrimaryParams) {
187
+ for (const key in this.inputPrimaryParams) {
188
+ if (this.inputPrimaryParams[key]) {
189
+ fn(this.inputPrimaryParams[key]);
190
+ }
191
+ }
192
+ }
56
193
  if (this.inputSecondary) {
57
194
  fn(this.inputSecondary);
195
+ if (this.inputSecondaryParams) {
196
+ for (const key in this.inputSecondaryParams) {
197
+ if (this.inputSecondaryParams[key]) {
198
+ fn(this.inputSecondaryParams[key]);
199
+ }
200
+ }
201
+ }
58
202
  }
59
203
  }
60
204
 
@@ -63,10 +207,36 @@ export default class ImageExpression implements Expression {
63
207
  return false;
64
208
  }
65
209
 
210
+ serializeParams(params: Record<string, Expression> | undefined): {params: Record<string, SerializedExpression>} {
211
+ const result: Record<string, SerializedExpression> = {};
212
+ if (params) {
213
+ for (const key in params) {
214
+ if (params[key]) {
215
+ result[key] = params[key].serialize();
216
+ }
217
+ }
218
+ } else {
219
+ return undefined;
220
+ }
221
+
222
+ return {params: result};
223
+ }
224
+
66
225
  serialize(): SerializedExpression {
226
+ const serialized: SerializedExpression = ["image", this.inputPrimary.serialize()];
227
+
228
+ if (this.inputPrimaryParams) {
229
+ serialized.push(this.serializeParams(this.inputPrimaryParams));
230
+ }
231
+
67
232
  if (this.inputSecondary) {
68
- return ["image", this.inputPrimary.serialize(), this.inputSecondary.serialize()];
233
+ serialized.push(this.inputSecondary.serialize());
234
+
235
+ if (this.inputSecondaryParams) {
236
+ serialized.push(this.serializeParams(this.inputSecondaryParams));
237
+ }
69
238
  }
70
- return ["image", this.inputPrimary.serialize()];
239
+
240
+ return serialized;
71
241
  }
72
242
  }
@@ -182,6 +182,13 @@ CompoundExpression.register(expressions, {
182
182
  return v.evaluate(ctx).toRenderColor(null).toArray();
183
183
  }
184
184
  ],
185
+ 'to-hsla': [
186
+ array(NumberType, 4),
187
+ [ColorType],
188
+ (ctx, [v]) => {
189
+ return v.evaluate(ctx).toRenderColor(null).toHslaArray();
190
+ }
191
+ ],
185
192
  'rgb': [
186
193
  ColorType,
187
194
  [NumberType, NumberType, NumberType],
@@ -1,8 +1,10 @@
1
1
  import UnitBezier from '@mapbox/unitbezier';
2
2
  import * as interpolate from '../../util/interpolate';
3
- import {toString, NumberType, ColorType} from '../types';
3
+ import {toString, NumberType, ColorType, ValueType} from '../types';
4
4
  import {findStopLessThanOrEqualTo} from '../stops';
5
5
  import {hcl, lab} from '../../util/color_spaces';
6
+ import Literal from './literal';
7
+ import RuntimeError from '../runtime_error';
6
8
 
7
9
  import type Color from '../../util/color';
8
10
  import type {Stops} from '../stops';
@@ -27,14 +29,16 @@ class Interpolate implements Expression {
27
29
  operator: 'interpolate' | 'interpolate-hcl' | 'interpolate-lab';
28
30
  interpolation: InterpolationType;
29
31
  input: Expression;
32
+ dynamicStops: Expression | null;
30
33
  labels: Array<number>;
31
34
  outputs: Array<Expression>;
32
35
 
33
- constructor(type: Type, operator: 'interpolate' | 'interpolate-hcl' | 'interpolate-lab', interpolation: InterpolationType, input: Expression, stops: Stops) {
36
+ constructor(type: Type, operator: 'interpolate' | 'interpolate-hcl' | 'interpolate-lab', interpolation: InterpolationType, input: Expression, dynamicStops: Expression | null, stops: Stops) {
34
37
  this.type = type;
35
38
  this.operator = operator;
36
39
  this.interpolation = interpolation;
37
40
  this.input = input;
41
+ this.dynamicStops = dynamicStops;
38
42
 
39
43
  this.labels = [];
40
44
  this.outputs = [];
@@ -101,12 +105,12 @@ class Interpolate implements Expression {
101
105
  return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
102
106
  }
103
107
 
104
- if (args.length - 1 < 4) {
108
+ if (args.length - 1 < 3) {
105
109
  // @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
106
- return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
110
+ return context.error(`Expected at least 3 arguments, but found only ${args.length - 1}.`);
107
111
  }
108
112
 
109
- if ((args.length - 1) % 2 !== 0) {
113
+ if (args.length - 1 > 3 && (args.length - 1) % 2 !== 0) {
110
114
  // @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
111
115
  return context.error(`Expected an even number of arguments.`);
112
116
  }
@@ -123,6 +127,15 @@ class Interpolate implements Expression {
123
127
  outputType = context.expectedType;
124
128
  }
125
129
 
130
+ // Exactly 3 arguments means that the steps are created by an expression
131
+ if (args.length - 1 === 3) {
132
+ const dynamicStops = context.parse(rest[0], 3, ValueType);
133
+ if (!dynamicStops) return null;
134
+
135
+ // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'InterpolationType'.
136
+ return new Interpolate(outputType, (operator as any), interpolation, input, dynamicStops, stops);
137
+ }
138
+
126
139
  for (let i = 0; i < rest.length; i += 2) {
127
140
  const label = rest[i];
128
141
  const value = rest[i + 1];
@@ -159,12 +172,36 @@ class Interpolate implements Expression {
159
172
  }
160
173
 
161
174
  // @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'InterpolationType'.
162
- return new Interpolate(outputType, (operator as any), interpolation, input, stops);
175
+ return new Interpolate(outputType, (operator as any), interpolation, input, null, stops);
163
176
  }
164
177
 
165
178
  evaluate(ctx: EvaluationContext): Color {
166
- const labels = this.labels;
167
- const outputs = this.outputs;
179
+ let labels = this.labels;
180
+ let outputs = this.outputs;
181
+
182
+ if (this.dynamicStops) {
183
+ const dynamicStopsValue = (this.dynamicStops.evaluate(ctx) as [number]);
184
+ if (dynamicStopsValue.length % 2 !== 0) {
185
+ throw new RuntimeError('Expected an even number of arguments.');
186
+ }
187
+ labels = [];
188
+ outputs = [];
189
+ for (let i = 0; i < dynamicStopsValue.length; i += 2) {
190
+ const label = dynamicStopsValue[i];
191
+ const output = new Literal(NumberType, dynamicStopsValue[i + 1]);
192
+ if (typeof label !== 'number') {
193
+ throw new RuntimeError('Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.');
194
+ }
195
+ if (labels.length && labels[labels.length - 1] >= label) {
196
+ throw new RuntimeError('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.');
197
+ }
198
+ labels.push(label);
199
+ outputs.push(output);
200
+ }
201
+ if (labels.length === 0) {
202
+ throw new RuntimeError('Expected at least one input/output pair.');
203
+ }
204
+ }
168
205
 
169
206
  if (labels.length === 1) {
170
207
  return outputs[0].evaluate(ctx);
@@ -225,11 +262,15 @@ class Interpolate implements Expression {
225
262
 
226
263
  const serialized = [this.operator, interpolation, this.input.serialize()];
227
264
 
228
- for (let i = 0; i < this.labels.length; i++) {
229
- serialized.push(
230
- this.labels[i],
231
- this.outputs[i].serialize()
232
- );
265
+ if (this.dynamicStops) {
266
+ serialized.push(this.dynamicStops.serialize());
267
+ } else {
268
+ for (let i = 0; i < this.labels.length; i++) {
269
+ serialized.push(
270
+ this.labels[i],
271
+ this.outputs[i].serialize()
272
+ );
273
+ }
233
274
  }
234
275
  return serialized;
235
276
  }
@@ -22,6 +22,7 @@ export type ExpressionParser = (args: ReadonlyArray<unknown>, context: ParsingCo
22
22
  export type ExpressionRegistration = {
23
23
  new(...args: any[]): Expression;
24
24
  readonly parse: ExpressionParser;
25
+ _classRegistryKey?: string;
25
26
  };
26
27
 
27
28
  export type ExpressionRegistry = {
@@ -16,7 +16,8 @@ import {
16
16
  supportsPropertyExpression,
17
17
  supportsZoomExpression,
18
18
  supportsLightExpression,
19
- supportsInterpolation
19
+ supportsInterpolation,
20
+ supportsLineProgressExpression
20
21
  } from '../util/properties';
21
22
  import {isFunction, createFunction} from '../function/index';
22
23
  import {Color} from './values';
@@ -71,6 +72,7 @@ export class StyleExpression {
71
72
  _defaultValue: Value;
72
73
  _warningHistory: {[key: string]: boolean};
73
74
  _enumValues?: {[_: string]: unknown};
75
+ configDependencies: Set<string>;
74
76
 
75
77
  constructor(expression: Expression, propertySpec?: StylePropertySpecification, scope?: string, options?: ConfigOptions) {
76
78
  this.expression = expression;
@@ -78,6 +80,7 @@ export class StyleExpression {
78
80
  this._evaluator = new EvaluationContext(scope, options);
79
81
  this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;
80
82
  this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;
83
+ this.configDependencies = isConstant.getConfigDependencies(expression);
81
84
  }
82
85
 
83
86
  evaluateWithoutErrorHandling(
@@ -183,11 +186,13 @@ export class ZoomConstantExpression<Kind extends EvaluationKind> {
183
186
  configDependencies: Set<string>;
184
187
  _styleExpression: StyleExpression;
185
188
  isLightConstant: boolean | null | undefined;
189
+ isLineProgressConstant: boolean | null | undefined;
186
190
 
187
- constructor(kind: Kind, expression: StyleExpression, isLightConstant?: boolean | null) {
191
+ constructor(kind: Kind, expression: StyleExpression, isLightConstant?: boolean | null, isLineProgressConstant?: boolean | null) {
188
192
  this.kind = kind;
189
193
  this._styleExpression = expression;
190
194
  this.isLightConstant = isLightConstant;
195
+ this.isLineProgressConstant = isLineProgressConstant;
191
196
  this.isStateDependent = kind !== ('constant' as EvaluationKind) && !isConstant.isStateConstant(expression.expression);
192
197
  this.configDependencies = isConstant.getConfigDependencies(expression.expression);
193
198
  }
@@ -220,17 +225,19 @@ export class ZoomDependentExpression<Kind extends EvaluationKind> {
220
225
  zoomStops: Array<number>;
221
226
  isStateDependent: boolean;
222
227
  isLightConstant: boolean | null | undefined;
228
+ isLineProgressConstant: boolean | null | undefined;
223
229
  configDependencies: Set<string>;
224
230
 
225
231
  _styleExpression: StyleExpression;
226
232
  interpolationType: InterpolationType | null | undefined;
227
233
 
228
- constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType, isLightConstant?: boolean | null) {
234
+ constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType, isLightConstant?: boolean | null, isLineProgressConstant?: boolean | null) {
229
235
  this.kind = kind;
230
236
  this.zoomStops = zoomStops;
231
237
  this._styleExpression = expression;
232
238
  this.isStateDependent = kind !== ('camera' as EvaluationKind) && !isConstant.isStateConstant(expression.expression);
233
239
  this.isLightConstant = isLightConstant;
240
+ this.isLineProgressConstant = isLineProgressConstant;
234
241
  this.configDependencies = isConstant.getConfigDependencies(expression.expression);
235
242
  this.interpolationType = interpolationType;
236
243
  }
@@ -282,6 +289,7 @@ export type SourceExpression = {
282
289
  kind: 'source';
283
290
  isStateDependent: boolean;
284
291
  isLightConstant: boolean | null | undefined;
292
+ isLineProgressConstant: boolean | null | undefined;
285
293
  configDependencies: Set<string>;
286
294
  readonly evaluate: (
287
295
  globals: GlobalProperties,
@@ -313,6 +321,7 @@ export interface CompositeExpression {
313
321
  kind: 'composite';
314
322
  isStateDependent: boolean;
315
323
  isLightConstant: boolean | null | undefined;
324
+ isLineProgressConstant: boolean | null | undefined;
316
325
  configDependencies: Set<string>;
317
326
  readonly evaluate: (
318
327
  globals: GlobalProperties,
@@ -360,6 +369,11 @@ export function createPropertyExpression(
360
369
  return error([new ParsingError('', 'measure-light expression not supported')]);
361
370
  }
362
371
 
372
+ const isLineProgressConstant = isConstant.isGlobalPropertyConstant(parsed, ['line-progress']);
373
+ if (!isLineProgressConstant && !supportsLineProgressExpression(propertySpec)) {
374
+ return error([new ParsingError('', 'line-progress expression not supported')]);
375
+ }
376
+
363
377
  const canRelaxZoomRestriction = propertySpec.expression && propertySpec.expression.relaxZoomRestriction;
364
378
  const zoomCurve = findZoomCurve(parsed);
365
379
  if (!zoomCurve && !isZoomConstant && !canRelaxZoomRestriction) {
@@ -371,20 +385,20 @@ export function createPropertyExpression(
371
385
  }
372
386
 
373
387
  if (!zoomCurve) {
374
- return success(isFeatureConstant ?
388
+ return success((isFeatureConstant && isLineProgressConstant) ?
375
389
  // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'unknown'.
376
- (new ZoomConstantExpression('constant', expression.value, isLightConstant) as ConstantExpression) :
390
+ (new ZoomConstantExpression('constant', expression.value, isLightConstant, isLineProgressConstant) as ConstantExpression) :
377
391
  // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'unknown'.
378
- (new ZoomConstantExpression('source', expression.value, isLightConstant) as SourceExpression));
392
+ (new ZoomConstantExpression('source', expression.value, isLightConstant, isLineProgressConstant) as SourceExpression));
379
393
  }
380
394
 
381
395
  const interpolationType = zoomCurve instanceof Interpolate ? zoomCurve.interpolation : undefined;
382
396
 
383
- return success(isFeatureConstant ?
397
+ return success((isFeatureConstant && isLineProgressConstant) ?
384
398
  // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'unknown'.
385
- (new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType, isLightConstant) as CameraExpression) :
399
+ (new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType, isLightConstant, isLineProgressConstant) as CameraExpression) :
386
400
  // @ts-expect-error - TS2339 - Property 'value' does not exist on type 'unknown'.
387
- (new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType, isLightConstant) as CompositeExpression));
401
+ (new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType, isLightConstant, isLineProgressConstant) as CompositeExpression));
388
402
  }
389
403
 
390
404
  // serialization wrapper for old-style stop functions normalized to the
@@ -32,7 +32,7 @@ export default class Formatted {
32
32
  isEmpty(): boolean {
33
33
  if (this.sections.length === 0) return true;
34
34
  return !this.sections.some(section => section.text.length !== 0 ||
35
- (section.image && section.image.namePrimary.length !== 0));
35
+ (section.image && section.image.namePrimary));
36
36
  }
37
37
 
38
38
  static factory(text: Formatted | string): Formatted {
@@ -0,0 +1,54 @@
1
+ import type {RasterizationOptions} from "./resolved_image";
2
+
3
+ export class ImageIdWithOptions {
4
+ id: string;
5
+ options: RasterizationOptions;
6
+
7
+ constructor(id: string, options?: RasterizationOptions) {
8
+ this.id = id;
9
+ this.options = options || {params: {}};
10
+
11
+ if (!this.options.transform) {
12
+ this.options.transform = new DOMMatrix([1, 0, 0, 1, 0, 0]);
13
+ } else {
14
+ const {a, b, c, d, e, f} = this.options.transform;
15
+ this.options.transform = new DOMMatrix([a, b, c, d, e, f]);
16
+ }
17
+ }
18
+
19
+ static deserializeFromString(serialized: string): ImageIdWithOptions {
20
+ const deserializedObject = JSON.parse(serialized);
21
+ const options: RasterizationOptions = {params: deserializedObject.options.params};
22
+
23
+ const {a, b, c, d, e, f} = deserializedObject.options.transform;
24
+
25
+ options.transform = new DOMMatrix([a, b, c, d, e, f]);
26
+
27
+ return new ImageIdWithOptions(deserializedObject.id, deserializedObject.options);
28
+ }
29
+
30
+ scaleSelf(factor: number): this {
31
+ this.options.transform = this.options.transform.scale(factor);
32
+ return this;
33
+ }
34
+
35
+ serialize(): string {
36
+ const serialisedObject: Record<string, any> = {
37
+ id: this.id,
38
+ };
39
+
40
+ if (this.options) {
41
+ serialisedObject.options = this.options;
42
+ }
43
+
44
+ const {
45
+ a, b, c, d, e, f,
46
+ } = this.options.transform;
47
+
48
+ serialisedObject.options.transform = {
49
+ a, b, c, d, e, f,
50
+ };
51
+
52
+ return JSON.stringify(serialisedObject);
53
+ }
54
+ }