mascot-vis 3.0.0 → 3.0.2
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.
- package/dist/mascot-esm.js +186 -0
- package/package.json +3 -6
- package/dist/mascot-es.js +0 -27745
- package/dist/mascot-min.js +0 -186
- package/dist/mascot-umd.js +0 -27781
- package/js/depGraphVis.js +0 -66
- package/src-new-ts/action/createElement.ts +0 -91
- package/src-new-ts/action/encode.js +0 -20
- package/src-new-ts/action/repeat.js +0 -128
- package/src-new-ts/action/traverseScene.js +0 -41
- package/src-new-ts/data/Network.js +0 -2
- package/src-new-ts/data/Scope.js +0 -135
- package/src-new-ts/data/Table.js +0 -263
- package/src-new-ts/data/Tree.js +0 -3
- package/src-new-ts/data/field.ts +0 -115
- package/src-new-ts/data/import.ts +0 -96
- package/src-new-ts/data/predicate.ts +0 -82
- package/src-new-ts/depgraph/DepGraph.js +0 -178
- package/src-new-ts/depgraph/Edge.js +0 -9
- package/src-new-ts/depgraph/SceneGraph2DepGraph.js +0 -110
- package/src-new-ts/depgraph/Signal.js +0 -12
- package/src-new-ts/depgraph/operator/BoundsEvaluator.js +0 -30
- package/src-new-ts/depgraph/operator/Dataflow.js +0 -41
- package/src-new-ts/depgraph/operator/DomainBuilder.js +0 -50
- package/src-new-ts/depgraph/updateDepGraph.js +0 -45
- package/src-new-ts/depgraph/variable/BoundsVar.js +0 -81
- package/src-new-ts/depgraph/variable/ChannelVar.js +0 -17
- package/src-new-ts/depgraph/variable/DataScopeVar.js +0 -12
- package/src-new-ts/depgraph/variable/DomainVar.js +0 -15
- package/src-new-ts/depgraph/variable/FieldVar.js +0 -17
- package/src-new-ts/depgraph/variable/LayoutParameter.js +0 -8
- package/src-new-ts/depgraph/variable/ScaleVar.js +0 -13
- package/src-new-ts/depgraph/variable/Variable.js +0 -39
- package/src-new-ts/element/gradient/LinearGradient.js +0 -37
- package/src-new-ts/element/group/Collection.js +0 -109
- package/src-new-ts/element/group/Group.js +0 -307
- package/src-new-ts/element/group/Scene.js +0 -98
- package/src-new-ts/element/mark/CircleMark.ts +0 -85
- package/src-new-ts/element/mark/Mark.ts +0 -233
- package/src-new-ts/element/mark/PathMark.js +0 -483
- package/src-new-ts/element/mark/Segment.js +0 -29
- package/src-new-ts/element/mark/Vertex.js +0 -118
- package/src-new-ts/encode/Scale.ts +0 -115
- package/src-new-ts/index.ts +0 -19
- package/src-new-ts/layout/Layout.ts +0 -3
- package/src-new-ts/render/CanvasRenderer.ts +0 -24
- package/src-new-ts/render/SVGRenderer.js +0 -316
- package/src-new-ts/util.ts +0 -3
- package/src-old/action/Classify.js +0 -53
- package/src-old/action/Densify.js +0 -199
- package/src-old/action/Partition.js +0 -531
- package/src-old/action/Repeat.js +0 -106
- package/src-old/action/Repopulate.js +0 -44
- package/src-old/action/Stratify.js +0 -156
- package/src-old/basic/Gradient.js +0 -37
- package/src-old/basic/Point.js +0 -51
- package/src-old/basic/Rectangle.js +0 -63
- package/src-old/bind/bindToAngle.js +0 -56
- package/src-old/bind/bindToAreaMark.js +0 -360
- package/src-old/bind/bindToColor.js +0 -114
- package/src-old/bind/bindToLink.js +0 -81
- package/src-old/bind/bindToPosition.js +0 -283
- package/src-old/bind/bindToRadialDistance.js +0 -62
- package/src-old/bind/bindToSize.js +0 -235
- package/src-old/bind/bindToText.js +0 -60
- package/src-old/bind/bindToThickness.js +0 -100
- package/src-old/constraint/AffixConstraint.js +0 -129
- package/src-old/constraint/AlignConstraint.js +0 -58
- package/src-old/core/Encoding.js +0 -336
- package/src-old/core/Scale.js +0 -322
- package/src-old/core/SceneLoader.js +0 -290
- package/src-old/core/SceneValidator.js +0 -232
- package/src-old/core/SpecExecutor.js +0 -113
- package/src-old/core/SpecGenerator.js +0 -350
- package/src-old/data/DataImporter.js +0 -64
- package/src-old/data/DataScope.js +0 -124
- package/src-old/data/DataTable.js +0 -338
- package/src-old/data/Network.js +0 -106
- package/src-old/data/Tree.js +0 -251
- package/src-old/data/transform/Bin.js +0 -46
- package/src-old/data/transform/Filter.js +0 -48
- package/src-old/data/transform/Groupby.js +0 -18
- package/src-old/data/transform/KDE.js +0 -58
- package/src-old/data/transform/Sort.js +0 -14
- package/src-old/data/transform/Split.js +0 -5
- package/src-old/data/transform/partition.js +0 -46
- package/src-old/history/UndoRedoStack +0 -0
- package/src-old/index.js +0 -271
- package/src-old/indexSVG.js +0 -259
- package/src-old/interaction/Interaction.js +0 -91
- package/src-old/interaction/MouseEvent.js +0 -8
- package/src-old/interaction/Selection.js +0 -9
- package/src-old/interaction/brush.js +0 -362
- package/src-old/item/Segment.js +0 -29
- package/src-old/item/Vertex.js +0 -118
- package/src-old/item/composite/Collection.js +0 -106
- package/src-old/item/composite/Glyph.js +0 -19
- package/src-old/item/composite/Group.js +0 -310
- package/src-old/item/composite/Scene.js +0 -1251
- package/src-old/item/mark/ArcPath.js +0 -181
- package/src-old/item/mark/AreaPath.js +0 -78
- package/src-old/item/mark/CirclePath.js +0 -102
- package/src-old/item/mark/EllipsePath.js +0 -5
- package/src-old/item/mark/Image.js +0 -101
- package/src-old/item/mark/LinkPath.js +0 -118
- package/src-old/item/mark/Mark.js +0 -163
- package/src-old/item/mark/Path.js +0 -494
- package/src-old/item/mark/PointText.js +0 -201
- package/src-old/item/mark/PolygonPath.js +0 -64
- package/src-old/item/mark/RectPath.js +0 -88
- package/src-old/item/mark/RingPath.js +0 -92
- package/src-old/item/refs/Axis.js +0 -362
- package/src-old/item/refs/EncodingAxis.js +0 -515
- package/src-old/item/refs/Gridlines.js +0 -144
- package/src-old/item/refs/LayoutAxis.js +0 -316
- package/src-old/item/refs/Legend.js +0 -273
- package/src-old/layout/Circular.js +0 -95
- package/src-old/layout/Force.js +0 -52
- package/src-old/layout/Grid.js +0 -423
- package/src-old/layout/Layout.js +0 -13
- package/src-old/layout/Packing.js +0 -56
- package/src-old/layout/Stack.js +0 -264
- package/src-old/layout/Strata.js +0 -88
- package/src-old/layout/Sugiyama.js +0 -59
- package/src-old/layout/TidyTree.js +0 -105
- package/src-old/layout/Treemap.js +0 -87
- package/src-old/renderer/SVGInteractionHandler.js +0 -241
- package/src-old/renderer/SVGRenderer.js +0 -325
- package/src-old/renderer/WebGLRenderer.js +0 -1097
- package/src-old/renderer/WebGLRenderer2.js +0 -249
- package/src-old/renderer/threejs/Line2.js +0 -18
- package/src-old/renderer/threejs/LineGeometry.js +0 -77
- package/src-old/renderer/threejs/LineMaterial.js +0 -605
- package/src-old/renderer/threejs/LineSegments2.js +0 -281
- package/src-old/renderer/threejs/LineSegmentsGeometry.js +0 -226
- package/src-old/renderer/threejs/Wireframe.js +0 -51
- package/src-old/renderer/threejs/WireframeGeometry2.js +0 -16
- package/src-old/scale/areaSize.js +0 -0
- package/src-old/scale/domain.js +0 -38
- package/src-old/util/Constants.js +0 -180
- package/src-old/util/DataUtil.js +0 -35
- package/src-old/util/ItemUtil.js +0 -586
- package/src-old/util/Numerical.js +0 -33
- package/tests/demo-tests/README.md +0 -80
- package/tests/demo-tests/SVG2PNG.js +0 -56
- package/tests/demo-tests/demos2CanvasPNGs.js +0 -69
- package/tests/demo-tests/demos2ScenesSVGs.js +0 -100
- package/tests/demo-tests/pathElementWorker.js +0 -91
- package/tests/demo-tests/pixelTest.js +0 -62
- package/tests/demo-tests/renderDemos.html +0 -132
- package/tests/demo-tests/serializationTest.js +0 -36
- package/tests/demo-tests/serializeDemos.html +0 -134
- package/tests/unit-tests/README.md +0 -4
- package/tests/unit-tests/jasmine-browser.json +0 -21
- package/tests/unit-tests/jasmine.json +0 -14
- package/tests/unit-tests/testSpec.js +0 -274
|
@@ -1,1251 +0,0 @@
|
|
|
1
|
-
import Group from "./Group";
|
|
2
|
-
import { ItemType, Errors, LayoutType, ConstraintType, DataType, Warnings, Orientation } from "../../util/Constants";
|
|
3
|
-
import { findItems, getParents, canAlign, getPeers, getEncodingKey, getClosestLayout, getPeersGroupedByParent, isPath, isGuide, isMark, ItemCounter, getAllChildren } from "../../util/ItemUtil";
|
|
4
|
-
import { validateField } from "../../util/DataUtil";
|
|
5
|
-
import { repeatItem, repeatNodeLink } from "../../action/Repeat";
|
|
6
|
-
import { divideItem } from "../../action/Partition";
|
|
7
|
-
import { densifyItem } from "../../action/Densify";
|
|
8
|
-
import Mark from "../mark/Mark";
|
|
9
|
-
import RectPath from "../mark/RectPath";
|
|
10
|
-
import CirclePath from "../mark/CirclePath";
|
|
11
|
-
import Path from "../mark/Path";
|
|
12
|
-
import { bindToSize } from "../../bind/bindToSize";
|
|
13
|
-
import { bindToPosition } from "../../bind/bindToPosition";
|
|
14
|
-
import { bindToRadialDistance } from "../../bind/bindToRadialDistance";
|
|
15
|
-
import { bindToColor } from "../../bind/bindToColor";
|
|
16
|
-
import { bindToAngle } from "../../bind/bindToAngle"
|
|
17
|
-
import { bindToAreaMark } from "../../bind/bindToAreaMark";
|
|
18
|
-
import { bindToText } from "../../bind/bindToText";
|
|
19
|
-
import Encoding from "../../core/Encoding";
|
|
20
|
-
import DataTable from "../../data/DataTable";
|
|
21
|
-
import EncodingAxis from "../refs/EncodingAxis";
|
|
22
|
-
import LayoutAxis from "../refs/LayoutAxis";
|
|
23
|
-
import Legend from "../refs/Legend";
|
|
24
|
-
import Glyph from "./Glyph";
|
|
25
|
-
import AreaPath from "../mark/AreaPath";
|
|
26
|
-
import RingPath from "../mark/RingPath";
|
|
27
|
-
import PolygonPath from "../mark/PolygonPath";
|
|
28
|
-
import AlignConstraint from "../../constraint/AlignConstraint";
|
|
29
|
-
import AffixConstraint from "../../constraint/AffixConstraint";
|
|
30
|
-
import PointText from "../mark/PointText";
|
|
31
|
-
import Gridlines from "../refs/Gridlines";
|
|
32
|
-
import Collection from "./Collection";
|
|
33
|
-
import DataScope from "../../data/DataScope";
|
|
34
|
-
import ArcPath from "../mark/ArcPath";
|
|
35
|
-
import Image from "../mark/Image";
|
|
36
|
-
import LinkPath from "../mark/LinkPath";
|
|
37
|
-
import { bindToLink } from "../../bind/bindToLink";
|
|
38
|
-
import SceneValidator from "../../core/SceneValidator";
|
|
39
|
-
import { repopulateItem } from "../../action/Repopulate";
|
|
40
|
-
import { bindToThickness } from "../../bind/bindToThickness";
|
|
41
|
-
import Tree from "../../data/Tree";
|
|
42
|
-
import { stratifyItem } from "../../action/Stratify";
|
|
43
|
-
import { classifiable, classifyCollectionChildren } from "../../action/Classify";
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export default class Scene extends Group {
|
|
47
|
-
|
|
48
|
-
constructor(args) {
|
|
49
|
-
super();
|
|
50
|
-
if (args && args.fillColor) {
|
|
51
|
-
this.fillColor = args.fillColor;
|
|
52
|
-
}
|
|
53
|
-
this.type = ItemType.Scene;
|
|
54
|
-
this._id = this.type + ItemCounter[this.type]++;
|
|
55
|
-
//this.cellAlign = {};
|
|
56
|
-
this._encodings = {};
|
|
57
|
-
this.constraints = {};
|
|
58
|
-
this._itemMap = {};
|
|
59
|
-
this._interactions = [];
|
|
60
|
-
this._interactionsObj = {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
clear() {
|
|
64
|
-
this.removeAll();
|
|
65
|
-
this._encodings = {};
|
|
66
|
-
this.constraints = {};
|
|
67
|
-
this._itemMap = {};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
group(children) {
|
|
71
|
-
let g = new Group();
|
|
72
|
-
g.classId = g.id;
|
|
73
|
-
this.addChild(g);
|
|
74
|
-
if (children && children.length > 0)
|
|
75
|
-
for (let c of children)
|
|
76
|
-
g.addChild(c);
|
|
77
|
-
this._itemMap[g.id] = g;
|
|
78
|
-
return g;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
mark(type, param) {
|
|
82
|
-
let args = param === undefined ? {} : param;
|
|
83
|
-
let m = null;
|
|
84
|
-
args.type = type;
|
|
85
|
-
switch (type) {
|
|
86
|
-
case ItemType.Rect: {
|
|
87
|
-
if (!("top" in args))
|
|
88
|
-
args.top = 0;
|
|
89
|
-
if (!("left" in args))
|
|
90
|
-
args.left = 0;
|
|
91
|
-
if (!("width" in args))
|
|
92
|
-
args.width = 100;
|
|
93
|
-
if (!("height" in args))
|
|
94
|
-
args.height = 100;
|
|
95
|
-
let top = args["top"], left = args["left"], width = args["width"], height = args["height"];
|
|
96
|
-
args.vertices = [[left, top], [left + width, top], [left + width, top + height], [left, top + height]];
|
|
97
|
-
delete args["top"];
|
|
98
|
-
delete args["left"];
|
|
99
|
-
delete args["width"];
|
|
100
|
-
delete args["height"];
|
|
101
|
-
if (!("fillColor" in args)) {
|
|
102
|
-
args.fillColor = "none";
|
|
103
|
-
}
|
|
104
|
-
m = new RectPath(args);
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
case ItemType.Area:
|
|
108
|
-
if (args !== undefined && "x1" in args && "y1" in args && "x2" in args && "y2" in args) {
|
|
109
|
-
let x1 = args["x1"], y1 = args["y1"], x2 = args["x2"], y2 = args["y2"];
|
|
110
|
-
// args.vertices contains all the vertices on two boundary lines
|
|
111
|
-
args.vertices = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]];
|
|
112
|
-
delete args["x1"];
|
|
113
|
-
delete args["y1"];
|
|
114
|
-
delete args["x2"];
|
|
115
|
-
delete args["y2"];
|
|
116
|
-
}
|
|
117
|
-
m = new AreaPath(args);
|
|
118
|
-
break;
|
|
119
|
-
case ItemType.Line: {
|
|
120
|
-
//it is possible to create a skeleton line without x1, y1, x2, y2 args, e.g. when duplicating
|
|
121
|
-
if (args !== undefined && "x1" in args && "y1" in args && "x2" in args && "y2" in args) {
|
|
122
|
-
let x1 = args["x1"], y1 = args["y1"], x2 = args["x2"], y2 = args["y2"];
|
|
123
|
-
args.vertices = [[x1, y1], [x2, y2]];
|
|
124
|
-
//remove x1, y1, x2, y2 and compute these values at rendering time
|
|
125
|
-
//so that we don't have to keep track of them when the line is transformed
|
|
126
|
-
delete args["x1"];
|
|
127
|
-
delete args["y1"];
|
|
128
|
-
delete args["x2"];
|
|
129
|
-
delete args["y2"];
|
|
130
|
-
}
|
|
131
|
-
m = new Path(args);
|
|
132
|
-
m.type = ItemType.Line;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case ItemType.Path:
|
|
136
|
-
m = new Path(args);
|
|
137
|
-
break;
|
|
138
|
-
case ItemType.Circle:
|
|
139
|
-
m = new CirclePath(args);
|
|
140
|
-
break;
|
|
141
|
-
case ItemType.Ring:
|
|
142
|
-
m = new RingPath(args);
|
|
143
|
-
break;
|
|
144
|
-
case ItemType.Arc:
|
|
145
|
-
case ItemType.Pie:
|
|
146
|
-
m = new ArcPath(args);
|
|
147
|
-
break;
|
|
148
|
-
case ItemType.Polygon:
|
|
149
|
-
m = new PolygonPath(args);
|
|
150
|
-
break;
|
|
151
|
-
// case ItemType.Pie:
|
|
152
|
-
// args.innerRadius = 0;
|
|
153
|
-
// if ("radius" in args) {
|
|
154
|
-
// args.outerRadius = args.radius;
|
|
155
|
-
// }
|
|
156
|
-
// m = new ArcPath(args);
|
|
157
|
-
// break;
|
|
158
|
-
case "text":
|
|
159
|
-
case ItemType.PointText:
|
|
160
|
-
if (!("anchor" in args))
|
|
161
|
-
args["anchor"] = ["center", "middle"];
|
|
162
|
-
m = new PointText(args);
|
|
163
|
-
break;
|
|
164
|
-
case ItemType.Image:
|
|
165
|
-
m = new Image(args);
|
|
166
|
-
break;
|
|
167
|
-
case ItemType.Link:
|
|
168
|
-
m = new LinkPath(args);
|
|
169
|
-
break;
|
|
170
|
-
default:
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
if (m !== null) {
|
|
174
|
-
if ("id" in args)
|
|
175
|
-
m.id = args.id;
|
|
176
|
-
else
|
|
177
|
-
m.id = m.type + ItemCounter[m.type]++;
|
|
178
|
-
m.classId = m.id;
|
|
179
|
-
this.addChild(m);
|
|
180
|
-
this._itemMap[m.id] = m;
|
|
181
|
-
}
|
|
182
|
-
return m;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
glyph(...args) {
|
|
186
|
-
let g = new Glyph(args);
|
|
187
|
-
g.classId = g.id;
|
|
188
|
-
this.addChild(g);
|
|
189
|
-
this._itemMap[g.id] = g;
|
|
190
|
-
return g;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
collection() {
|
|
194
|
-
let c = new Collection();
|
|
195
|
-
this.addChild(c);
|
|
196
|
-
this._itemMap[c.id] = c;
|
|
197
|
-
return c;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
attach(item, table) {
|
|
201
|
-
item.dataScope = new DataScope(table);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
repeat(item, data, param) {
|
|
205
|
-
if (!item || data === undefined) {
|
|
206
|
-
throw Errors.INCOMPLETE_REPEAT_INFO;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (data.nodeTable && data.linkTable) {
|
|
210
|
-
if (!Array.isArray(item) || item.length !== 2)
|
|
211
|
-
throw Errors.REPEAT_NODE_LINK;
|
|
212
|
-
return repeatNodeLink(this, item[0], item[1], data);
|
|
213
|
-
} else {
|
|
214
|
-
let args = param ? param : {},
|
|
215
|
-
field = args["field"] ? args["field"] : DataTable.RowID,
|
|
216
|
-
callback = args["callback"];
|
|
217
|
-
|
|
218
|
-
validateField(field, data);
|
|
219
|
-
|
|
220
|
-
let collection = repeatItem(this, item, field, data, callback);
|
|
221
|
-
if (args.layout)
|
|
222
|
-
collection.layout = args.layout;
|
|
223
|
-
return collection;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
repopulate(item, table, param) {
|
|
228
|
-
if (!item || table === undefined) {
|
|
229
|
-
throw Errors.INCOMPLETE_REPEAT_INFO;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let args = param ? param : {},
|
|
233
|
-
field = args["field"] ? args["field"] : DataTable.RowID;
|
|
234
|
-
|
|
235
|
-
validateField(field, table);
|
|
236
|
-
repopulateItem(this, item, field, table);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
densify(item, table, param) {
|
|
240
|
-
if (!item || table === undefined) {
|
|
241
|
-
throw Errors.INCOMPLETE_PARTITION_INFO;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
let args = param ? param : {},
|
|
245
|
-
orientation = args["orientation"],
|
|
246
|
-
field = args["field"] ? args["field"] : DataTable.RowID,
|
|
247
|
-
//following two are for circle densification
|
|
248
|
-
startAngle = "startAngle" in args ? args["startAngle"] : 90,
|
|
249
|
-
direction = 'direction' in args ? args["direction"] : "clockwise",
|
|
250
|
-
callback = args["callback"];
|
|
251
|
-
|
|
252
|
-
validateField(field, table);
|
|
253
|
-
|
|
254
|
-
let newItem = densifyItem(this, item, orientation, field, table, callback, startAngle, direction);
|
|
255
|
-
SceneValidator.markDensified(item, newItem);
|
|
256
|
-
return newItem;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
stratify(item, data, param) {
|
|
260
|
-
if (!(data instanceof Tree)) {
|
|
261
|
-
throw Errors.STRATIFY_WITHOUT_TREE;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (item.type !== ItemType.Circle && item.type !== ItemType.Rect) {
|
|
265
|
-
throw Errors.STRATIFY_WRONG_ITEM;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
let args = param ? param : {},
|
|
269
|
-
direction = args["direction"],
|
|
270
|
-
size = args["size"];
|
|
271
|
-
|
|
272
|
-
let collection = stratifyItem(this, item, direction, size, data);
|
|
273
|
-
//SceneValidator.markDivided(item, collection);
|
|
274
|
-
return collection;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
divide(item, data, param) {
|
|
278
|
-
if (!item || data == undefined) {
|
|
279
|
-
throw Errors.INCOMPLETE_PARTITION_INFO;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let args = param ? param : {},
|
|
283
|
-
orientation = args["orientation"],
|
|
284
|
-
field = args["field"] ? args["field"] : DataTable.RowID,
|
|
285
|
-
callback = args["callback"];
|
|
286
|
-
|
|
287
|
-
validateField(field, data);
|
|
288
|
-
|
|
289
|
-
let collection = divideItem(this, item, orientation, field, data, callback);
|
|
290
|
-
if (args.layout)
|
|
291
|
-
collection.layout = args.layout;
|
|
292
|
-
|
|
293
|
-
SceneValidator.markDivided(item, collection);
|
|
294
|
-
return collection;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
classify(item, param) {
|
|
298
|
-
if (!classifiable(item)) {
|
|
299
|
-
throw Errors.CANNOT_CLASSIFY + item.type;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
let args = param ? param : {},
|
|
303
|
-
field = args["field"] ? args["field"] : DataTable.RowID,
|
|
304
|
-
dt = item.firstChild.dataScope.dataTable,
|
|
305
|
-
layout = args.layout;
|
|
306
|
-
validateField(field, dt);
|
|
307
|
-
|
|
308
|
-
classifyCollectionChildren(this, item, field, layout);
|
|
309
|
-
|
|
310
|
-
//SceneValidator.markDivided(item, collection);
|
|
311
|
-
return item;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
_validateEncodeArgs(item, args) {
|
|
315
|
-
if (!item || !("channel" in args) || !("field" in args)) {
|
|
316
|
-
throw Errors.INCOMPLETE_BINDING_INFO;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
let field = args["field"];
|
|
320
|
-
|
|
321
|
-
//check if can apply encoding
|
|
322
|
-
if (item.type == "vertex" || item.type == "segment") {
|
|
323
|
-
if (!item.parent.dataScope && !item.dataScope) {
|
|
324
|
-
throw Errors.BIND_WITHOUT_DATASCOPE;
|
|
325
|
-
}
|
|
326
|
-
} else if (!item.dataScope) {
|
|
327
|
-
throw Errors.BIND_WITHOUT_DATASCOPE;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
let datatable = args.table ? args.table : item.dataScope ? item.dataScope._dt : item.parent.dataScope._dt;
|
|
332
|
-
|
|
333
|
-
let ft = datatable.getFieldType(field);
|
|
334
|
-
if (args.channel === "fillGradient" && [DataType.String, DataType.Boolean, DataType.Date].indexOf(ft) >= 0)
|
|
335
|
-
throw Errors.UNSUPPORTED_FIELDTYPE + args.channel + ": " + ft;
|
|
336
|
-
|
|
337
|
-
validateField(field, datatable);
|
|
338
|
-
//todo: validate channel
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
//supports encoding one single item or an array of items (which will use the same scale)
|
|
342
|
-
encode(itm, args) {
|
|
343
|
-
let arr = Array.isArray(itm) ? itm : [itm];
|
|
344
|
-
let encs = [], scale;
|
|
345
|
-
for (let item of arr) {
|
|
346
|
-
let items;
|
|
347
|
-
if ([ItemType.Pie, ItemType.Arc].indexOf(itm.type) >= 0 && args.channel === "angle") {
|
|
348
|
-
let parent = itm.parent;
|
|
349
|
-
if (parent.type === ItemType.Collection && parent.layout && parent.layout.orientation === Orientation.Angular) {
|
|
350
|
-
encs.push(this._encodeWithinCollection(item, args));
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
this._validateEncodeArgs(item, args);
|
|
355
|
-
if (item.type == "vertex" && item.parent.type == ItemType.Area) {
|
|
356
|
-
let areas = getPeers(item.parent, this);
|
|
357
|
-
items = [];
|
|
358
|
-
let firstHalf = item.parent.vertices.indexOf(item) < item.parent.vertices.length / 2;
|
|
359
|
-
for (let area of areas) {
|
|
360
|
-
items = firstHalf ? items.concat(area.vertices.slice(0, area.vertices.length / 2)) : items.concat(area.vertices.slice(area.vertices.length / 2));
|
|
361
|
-
}
|
|
362
|
-
} else {
|
|
363
|
-
items = getPeers(item, this);
|
|
364
|
-
}
|
|
365
|
-
if (items.length === 1 && items[0].type === ItemType.Area && (args.channel === "x" || args.channel === "y")) {
|
|
366
|
-
console.warn("Only 1 " + item.type + " found, cannot encode " + args.field);
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (!args.scale && scale) {
|
|
370
|
-
args.scale = scale;
|
|
371
|
-
}
|
|
372
|
-
let enc = this._doEncode(items, args);
|
|
373
|
-
if (!scale)
|
|
374
|
-
scale = enc.scale;
|
|
375
|
-
encs.push(enc);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return encs.length > 1 ? encs : encs[0];
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
_doEncode(items, args) {
|
|
382
|
-
let item = items[0],
|
|
383
|
-
channel = args["channel"],
|
|
384
|
-
field = args["field"];
|
|
385
|
-
|
|
386
|
-
if (!("datatable" in args))
|
|
387
|
-
args.datatable = item.dataScope ? item.dataScope.dataTable : item.parent.dataScope.dataTable;
|
|
388
|
-
if (!("aggregator" in args))
|
|
389
|
-
args.aggregator = "mean";
|
|
390
|
-
if (!("flipScale" in args))
|
|
391
|
-
args.flipScale = false;
|
|
392
|
-
if (!("includeZero" in args))
|
|
393
|
-
args.includeZero = false;
|
|
394
|
-
if (!("scaleType" in args))
|
|
395
|
-
args.scaleType = "linear";
|
|
396
|
-
|
|
397
|
-
let encoding = new Encoding(items, this, channel, field, args);
|
|
398
|
-
switch (channel) {
|
|
399
|
-
case "width":
|
|
400
|
-
case "height":
|
|
401
|
-
case "radius":
|
|
402
|
-
case "outerRadius":
|
|
403
|
-
case "innerRadius":
|
|
404
|
-
case "area":
|
|
405
|
-
case "fontSize":
|
|
406
|
-
if (item.type == ItemType.Area)
|
|
407
|
-
bindToAreaMark(encoding);
|
|
408
|
-
else
|
|
409
|
-
bindToSize(encoding);
|
|
410
|
-
break;
|
|
411
|
-
case "strokeWidth":
|
|
412
|
-
if (item.type == ItemType.Link)
|
|
413
|
-
bindToLink(encoding);
|
|
414
|
-
else
|
|
415
|
-
bindToSize(encoding);
|
|
416
|
-
break;
|
|
417
|
-
case "x":
|
|
418
|
-
case "y":
|
|
419
|
-
bindToPosition(encoding);
|
|
420
|
-
// if (item.type == ItemType.Area)
|
|
421
|
-
// bindToAreaMark(encoding);
|
|
422
|
-
// else
|
|
423
|
-
// bindToPosition(encoding);
|
|
424
|
-
break;
|
|
425
|
-
case "fillColor":
|
|
426
|
-
case "strokeColor":
|
|
427
|
-
bindToColor(encoding);
|
|
428
|
-
break;
|
|
429
|
-
case "fillGradient":
|
|
430
|
-
if (item.type === ItemType.Area)
|
|
431
|
-
bindToAreaMark(encoding);
|
|
432
|
-
break;
|
|
433
|
-
case "angle":
|
|
434
|
-
bindToAngle(encoding);
|
|
435
|
-
break;
|
|
436
|
-
case "text":
|
|
437
|
-
bindToText(encoding);
|
|
438
|
-
break;
|
|
439
|
-
case "radialDistance":
|
|
440
|
-
// encoding.x = item.parent.x;
|
|
441
|
-
// encoding.y = item.parent.y;
|
|
442
|
-
// encoding.radius = item.parent.radius;
|
|
443
|
-
bindToRadialDistance(encoding);
|
|
444
|
-
break;
|
|
445
|
-
case "thickness":
|
|
446
|
-
bindToThickness(encoding);
|
|
447
|
-
break;
|
|
448
|
-
default:
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (!("_remember" in args) || args._remember == true)
|
|
453
|
-
this._registerBinding(encoding);
|
|
454
|
-
|
|
455
|
-
if (channel.indexOf("Color") < 0)
|
|
456
|
-
this._updateAncestorBounds(item, encoding.items);
|
|
457
|
-
|
|
458
|
-
return encoding;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
_encodeWithinCollection(item, args) {
|
|
462
|
-
this._validateEncodeArgs(item, args);
|
|
463
|
-
let peersByGroup = getPeersGroupedByParent(item, this);
|
|
464
|
-
let encs = [];
|
|
465
|
-
for (let g of peersByGroup) {
|
|
466
|
-
let enc = this._doEncode(g, args);
|
|
467
|
-
encs.push(enc);
|
|
468
|
-
}
|
|
469
|
-
return encs;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
getPeers(item) {
|
|
473
|
-
return getPeers(item, this);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
find(predicates) {
|
|
477
|
-
return findItems(this, predicates);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
align(items, direction) {
|
|
481
|
-
//TODO: check the existing constraints and encodings and see if there's any conflict
|
|
482
|
-
//if so, do nothing and return false
|
|
483
|
-
if (!canAlign(items, direction, this)) return false;
|
|
484
|
-
let c = new AlignConstraint(items, direction);
|
|
485
|
-
if (c.id in this.constraints) {
|
|
486
|
-
console.warn('constraint has been added');
|
|
487
|
-
return false;
|
|
488
|
-
}
|
|
489
|
-
this.constraints[c.id] = c;
|
|
490
|
-
c.apply();
|
|
491
|
-
this._updateAncestorBounds(items[0]);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
removeAllConstraints() {
|
|
495
|
-
this.constraints = {};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
removeConstraint(c) {
|
|
499
|
-
delete this.constraints[c.id];
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
getRelatedConstraints(items) {
|
|
503
|
-
let cstrs = [];
|
|
504
|
-
for (let cid in this.constraints) {
|
|
505
|
-
let c = this.constraints[cid];
|
|
506
|
-
switch (c.type) {
|
|
507
|
-
case ConstraintType.Align:
|
|
508
|
-
if (c._itemIds === items.map(d => d.classId).sort().join("_"))
|
|
509
|
-
cstrs.push(c);
|
|
510
|
-
break;
|
|
511
|
-
case ConstraintType.Affix:
|
|
512
|
-
break;
|
|
513
|
-
default:
|
|
514
|
-
break;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return cstrs;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// alignInCell(item, direction) {
|
|
521
|
-
// //TODO replace grid.vertCellAlignment and grid.horzCellAlignment
|
|
522
|
-
// }
|
|
523
|
-
|
|
524
|
-
affix(item, baseItem, channel, param) {
|
|
525
|
-
let args = param ? param : {},
|
|
526
|
-
offset = "offset" in args ? args.offset : 0,
|
|
527
|
-
itemAnchor = "itemAnchor" in args ? args.itemAnchor : (channel == "x" || channel == "angle") ? "center" : "middle",
|
|
528
|
-
baseAnchor = "baseAnchor" in args ? args.baseAnchor : (channel == "x" || channel == "angle") ? "center" : "middle";
|
|
529
|
-
let c = new AffixConstraint(item, baseItem, this, channel, itemAnchor, baseAnchor, offset);
|
|
530
|
-
if (c.id in this.constraints) {
|
|
531
|
-
//TODO: throw warning the constraint has been added, return false;
|
|
532
|
-
}
|
|
533
|
-
this.constraints[c.id] = c;
|
|
534
|
-
c.apply();
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
//arguments include a channel (x, y, width, height for now)
|
|
538
|
-
//optional arguments include orientation, x-coordinate, y-coordinate, tickFormat, strokeColor,
|
|
539
|
-
axis(channel, field, params) {
|
|
540
|
-
//need to figure out if item has the corresponding encoding, or if item position is determined by layout
|
|
541
|
-
let args = params ? params : {}, enc = args.item ? this.getEncodingByItem(args.item, channel) : this.getEncodingByField(field, channel);
|
|
542
|
-
if (enc && enc.field === field) {
|
|
543
|
-
if (enc.datatable.getFieldType(field) === DataType.Date && !("labelFormat" in args)) {
|
|
544
|
-
args.labelFormat = "%m/%d/%y";
|
|
545
|
-
}
|
|
546
|
-
if (enc.channel === "width") {
|
|
547
|
-
let existingChannels = this.children.filter(d => d.type === ItemType.Axis).map(d => d.channel);
|
|
548
|
-
if (!("orientation" in args))
|
|
549
|
-
args.orientation = existingChannels.indexOf("x") >= 0 ? "top" : "bottom";
|
|
550
|
-
} else if (enc.channel === "height") {
|
|
551
|
-
let existingChannels = this.children.filter(d => d.type === ItemType.Axis).map(d => d.channel);
|
|
552
|
-
if (!("orientation" in args))
|
|
553
|
-
args.orientation = existingChannels.indexOf("y") >= 0 ? "right" : "left";
|
|
554
|
-
}
|
|
555
|
-
let axis = new EncodingAxis(enc, args.item ? args.item : enc.anyItem, args);
|
|
556
|
-
if ("tickValues" in args) {
|
|
557
|
-
axis.tickValues = args["tickValues"];
|
|
558
|
-
axis.labelValues = args["tickValues"];
|
|
559
|
-
} else {
|
|
560
|
-
axis.tickValues = enc._inferTickValues();
|
|
561
|
-
axis.labelValues = enc._inferTickValues();
|
|
562
|
-
}
|
|
563
|
-
//TODO: need to remove axis when encoding changes or axis is removed
|
|
564
|
-
enc._axes.push(axis);
|
|
565
|
-
|
|
566
|
-
this.addChildAt(axis, 0);
|
|
567
|
-
this._itemMap[axis.id] = axis;
|
|
568
|
-
this._updateBounds();
|
|
569
|
-
return axis;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
//TODO: find out item from the field
|
|
573
|
-
let item = args.item ? args.item : findItems(this, [{ "field": field }])[0];
|
|
574
|
-
if (item === undefined) {
|
|
575
|
-
console.warn(Warnings.INCORRECT_AXIS_INFO + field);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
let layout = getClosestLayout(item);
|
|
579
|
-
if (!layout || (layout.type !== LayoutType.Grid && layout.type !== LayoutType.Stack)) return;
|
|
580
|
-
|
|
581
|
-
if (layout && (channel == "x" || channel == "y")) {
|
|
582
|
-
let group = layout.group,
|
|
583
|
-
groups = getPeers(group, this);
|
|
584
|
-
let axis, classId;
|
|
585
|
-
//TODO: think about how to specify if mutliple axes need to be created properly
|
|
586
|
-
// if (args.item)
|
|
587
|
-
// groups = [args.item.parent];
|
|
588
|
-
for (let c of groups) {
|
|
589
|
-
let itm = findItems(c, [{ "field": field }])[0];
|
|
590
|
-
let items = getPeers(itm, c);
|
|
591
|
-
if (itm.dataScope.dataTable.getFieldType(field) === DataType.Date && !("labelFormat" in args)) {
|
|
592
|
-
args.labelFormat = "%m/%d/%y";
|
|
593
|
-
}
|
|
594
|
-
axis = new LayoutAxis(items, c.layout, channel, field, args);
|
|
595
|
-
if (classId == undefined)
|
|
596
|
-
classId = axis.id;
|
|
597
|
-
axis.classId = classId;
|
|
598
|
-
this.addChildAt(axis, 0);
|
|
599
|
-
this._itemMap[axis.id] = axis;
|
|
600
|
-
// break;
|
|
601
|
-
}
|
|
602
|
-
this._updateBounds();
|
|
603
|
-
return axis;
|
|
604
|
-
} else {
|
|
605
|
-
console.warn(Warnings.INCORRECT_AXIS_INFO + field);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
getRelatedAxes(item) {
|
|
610
|
-
let axes = [];
|
|
611
|
-
if (isGuide(item))
|
|
612
|
-
return axes;
|
|
613
|
-
for (let c of this.children) {
|
|
614
|
-
if (c.type !== ItemType.Axis) continue;
|
|
615
|
-
if (c.matches(item)) {
|
|
616
|
-
axes.push(c);
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
//handle layout axis for small multiples
|
|
620
|
-
let p = c._item, found = false;
|
|
621
|
-
while (p.children && p.children.length > 0) {
|
|
622
|
-
for (let ic of p.children) {
|
|
623
|
-
if (ic.classId === getEncodingKey(item).split("_")[0]) {
|
|
624
|
-
found = true;
|
|
625
|
-
axes.push(c);
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
if (found) break;
|
|
630
|
-
else p = p.children[0];
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
//handle axis if the updated item is a group/collection
|
|
634
|
-
let allChildren = getAllChildren(item);
|
|
635
|
-
let map = {};
|
|
636
|
-
for (let i of allChildren) {
|
|
637
|
-
if (i.classId in map) continue;
|
|
638
|
-
map[i.classId] = i;
|
|
639
|
-
}
|
|
640
|
-
let toCheck = Object.values(map);
|
|
641
|
-
for (let i of toCheck) {
|
|
642
|
-
if (c.matches(i)) {
|
|
643
|
-
axes.push(c);
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
// while (p.children && p.children.length > 0) {
|
|
648
|
-
// for (let ic of p.children) {
|
|
649
|
-
// if (c.matches(ic)) {
|
|
650
|
-
// found = true;
|
|
651
|
-
// axes.push(c);
|
|
652
|
-
// break;
|
|
653
|
-
// }
|
|
654
|
-
// }
|
|
655
|
-
// if (found) break;
|
|
656
|
-
// else p = p.children[0];
|
|
657
|
-
// }
|
|
658
|
-
}
|
|
659
|
-
return axes;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
removeItem(itm) {
|
|
663
|
-
if (!isGuide(itm)) {
|
|
664
|
-
let encodings = this.getRelatedEncodings(itm);
|
|
665
|
-
for (let enc of encodings) {
|
|
666
|
-
this.removeEncoding(enc);
|
|
667
|
-
}
|
|
668
|
-
//TODO: remove relevant graphical constraints
|
|
669
|
-
let axes = this.getRelatedAxes(itm);
|
|
670
|
-
for (let a of axes)
|
|
671
|
-
this.removeItem(a);
|
|
672
|
-
for (let g of this.getRelatedGridlines(itm))
|
|
673
|
-
this.removeItem(g);
|
|
674
|
-
}
|
|
675
|
-
delete this._itemMap[itm.id];
|
|
676
|
-
this.removeChild(itm);
|
|
677
|
-
this._updateBounds();
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
//this is used to handle item deletion, assuming that item's parent is scene
|
|
681
|
-
getRelatedEncodings(item) {
|
|
682
|
-
let encodingKeys = [];
|
|
683
|
-
let itm = item;
|
|
684
|
-
while (itm) {
|
|
685
|
-
encodingKeys.push(getEncodingKey(itm));
|
|
686
|
-
if (itm.type == ItemType.Collection) {
|
|
687
|
-
itm = itm.firstChild;
|
|
688
|
-
} else if (itm.type == ItemType.Glyph) {
|
|
689
|
-
for (let c of itm.children) {
|
|
690
|
-
encodingKeys.push(getEncodingKey(c));
|
|
691
|
-
}
|
|
692
|
-
itm = undefined;
|
|
693
|
-
} else {
|
|
694
|
-
itm = undefined;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
let keys = Object.keys(this._encodings), encodings = [];
|
|
698
|
-
for (let k of keys) {
|
|
699
|
-
let itemKey = k.split("_")[0];
|
|
700
|
-
if (encodingKeys.indexOf(itemKey) >= 0) {
|
|
701
|
-
for (let channel in this._encodings[k])
|
|
702
|
-
encodings.push(this._encodings[k][channel]);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
return encodings;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
removeAllEncodings() {
|
|
709
|
-
this._encodings = {};
|
|
710
|
-
this.removeAllItemsByType(ItemType.Axis);
|
|
711
|
-
this.removeAllItemsByType(ItemType.Legend);
|
|
712
|
-
this.removeAllItemsByType(ItemType.Gridlines);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
removeEncoding(enc) {
|
|
716
|
-
let key = getEncodingKey(enc.anyItem);
|
|
717
|
-
delete this._encodings[key][enc.channel];
|
|
718
|
-
if (Object.keys(this._encodings[key]).length === 0)
|
|
719
|
-
delete this._encodings[key];
|
|
720
|
-
let toRemove = [];
|
|
721
|
-
for (let c of this.children) {
|
|
722
|
-
if (isGuide(c) && c.encoding && c.encoding === enc) {
|
|
723
|
-
toRemove.push(c);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
for (let a of toRemove) {
|
|
727
|
-
this.removeItem(a);
|
|
728
|
-
}
|
|
729
|
-
this._updateBounds();
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
removeAllItemsByType(type) {
|
|
733
|
-
let toRemove = [];
|
|
734
|
-
for (let a of this.children) {
|
|
735
|
-
if (a.type === type) {
|
|
736
|
-
toRemove.push(a);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
for (let i of toRemove)
|
|
740
|
-
this.removeItem(i);
|
|
741
|
-
this._updateBounds();
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
reCreateRelatedAxes(item) {
|
|
745
|
-
let axes = this.getRelatedAxes(item);
|
|
746
|
-
let layoutAxisClassIds = [];
|
|
747
|
-
axes.forEach(a => {
|
|
748
|
-
let args = a.toJSON().args;
|
|
749
|
-
if (args.item)
|
|
750
|
-
args.item = this.getItem(args.item);
|
|
751
|
-
delete args["tickValues"];
|
|
752
|
-
delete args["labelValues"];
|
|
753
|
-
this.removeItem(a);
|
|
754
|
-
if (a instanceof LayoutAxis) {
|
|
755
|
-
if (layoutAxisClassIds.indexOf(a.classId) < 0) {
|
|
756
|
-
this.axis(a.channel, a.field, args);
|
|
757
|
-
layoutAxisClassIds.push(a.classId);
|
|
758
|
-
}
|
|
759
|
-
} else {
|
|
760
|
-
this.axis(a.channel, a.field, args);
|
|
761
|
-
}
|
|
762
|
-
//toCreate.push({channel: a.channel, field: a.field, args: a.args});
|
|
763
|
-
});
|
|
764
|
-
this._updateBounds();
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
reCreateRelatedLegends(item) {
|
|
768
|
-
let lgds = this.getRelatedLegends(item);
|
|
769
|
-
lgds.forEach(a => {
|
|
770
|
-
let args = a.toJSON().args;
|
|
771
|
-
if (args.item)
|
|
772
|
-
args.item = this.getItem(args.item);
|
|
773
|
-
this.removeItem(a);
|
|
774
|
-
this.legend(a.channel, a.field, args);
|
|
775
|
-
});
|
|
776
|
-
this._updateBounds();
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
legend(channel, field, param) {
|
|
780
|
-
let args = param ? param : {};
|
|
781
|
-
let enc = this.getEncodingByField(field, channel);
|
|
782
|
-
if (!enc)
|
|
783
|
-
throw Errors.INCORRECT_LEGEND_INFO + field;
|
|
784
|
-
|
|
785
|
-
if (!("x" in args))
|
|
786
|
-
args["x"] = 100;
|
|
787
|
-
if (!("y" in args))
|
|
788
|
-
args["y"] = 100;
|
|
789
|
-
let legend = new Legend(enc, args);
|
|
790
|
-
this.addChild(legend);
|
|
791
|
-
this._itemMap[legend.id] = legend;
|
|
792
|
-
this._updateBounds();
|
|
793
|
-
return legend;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
gridlines(channel, field, params) {
|
|
797
|
-
let enc = this.getEncodingByField(field, channel), args = params ? params : {};
|
|
798
|
-
if (!enc) return false;
|
|
799
|
-
let gl = new Gridlines(enc, args.item ? args.item : enc.anyItem, args);
|
|
800
|
-
|
|
801
|
-
if ("values" in args) {
|
|
802
|
-
gl.values = args["values"];
|
|
803
|
-
} else {
|
|
804
|
-
gl.values = enc._inferTickValues();
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
this.addChildAt(gl, 0);
|
|
808
|
-
this._itemMap[gl.id] = gl;
|
|
809
|
-
this._updateBounds();
|
|
810
|
-
return gl;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
getRelatedLegends(item) {
|
|
814
|
-
let lg = [];
|
|
815
|
-
if (isGuide(item)) return lg;
|
|
816
|
-
for (let c of this.children) {
|
|
817
|
-
if (c.type !== ItemType.Legend) continue;
|
|
818
|
-
if (c.matches(item)) {
|
|
819
|
-
lg.push(c);
|
|
820
|
-
continue;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
return lg;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
getRelatedGridlines(item) {
|
|
827
|
-
let gl = [];
|
|
828
|
-
if (isGuide(item))
|
|
829
|
-
return gl;
|
|
830
|
-
for (let c of this.children) {
|
|
831
|
-
if (c.type !== ItemType.Gridlines) continue;
|
|
832
|
-
if (c.matches(item)) {
|
|
833
|
-
gl.push(c);
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
//handle layout axis for small multiples
|
|
837
|
-
let p = c._item, found = false;
|
|
838
|
-
while (p.children && p.children.length > 0) {
|
|
839
|
-
for (let ic of p.children) {
|
|
840
|
-
if (ic.classId === item.classId) {
|
|
841
|
-
found = true;
|
|
842
|
-
gl.push(c);
|
|
843
|
-
break;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
if (found) break;
|
|
847
|
-
else p = p.children[0];
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
//handle axis if the updated item is a group/collection
|
|
851
|
-
let allChildren = getAllChildren(item);
|
|
852
|
-
let map = {};
|
|
853
|
-
for (let i of allChildren) {
|
|
854
|
-
if (i.classId in map) continue;
|
|
855
|
-
map[i.classId] = i;
|
|
856
|
-
}
|
|
857
|
-
let toCheck = Object.values(map);
|
|
858
|
-
for (let i of toCheck) {
|
|
859
|
-
if (c.matches(i)) {
|
|
860
|
-
gl.push(c);
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
// p = item, found = false;
|
|
865
|
-
// while (p.children && p.children.length > 0) {
|
|
866
|
-
// for (let ic of p.children) {
|
|
867
|
-
// if (c.matches(ic)) {
|
|
868
|
-
// found = true;
|
|
869
|
-
// gl.push(c);
|
|
870
|
-
// break;
|
|
871
|
-
// }
|
|
872
|
-
// }
|
|
873
|
-
// if (found) break;
|
|
874
|
-
// else p = p.children[0];
|
|
875
|
-
// }
|
|
876
|
-
}
|
|
877
|
-
return gl;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
propagate(item, method, ...args) {
|
|
881
|
-
let peers = getPeers(item, this);
|
|
882
|
-
for (let p of peers)
|
|
883
|
-
p[method](...args);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
getEncodingByItem(item, channel) {
|
|
887
|
-
let enc = this._encodings[getEncodingKey(item)];
|
|
888
|
-
if (enc && enc[channel]) {
|
|
889
|
-
return enc[channel];
|
|
890
|
-
} else
|
|
891
|
-
return null;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
getEncodingByField(field, channel) {
|
|
895
|
-
for (let itmKey in this._encodings) {
|
|
896
|
-
let enc = this._encodings[itmKey];
|
|
897
|
-
if (enc[channel] && enc[channel].field == field)
|
|
898
|
-
return enc[channel];
|
|
899
|
-
}
|
|
900
|
-
return null;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
//item can be a mark, a group, a vertex or a segment
|
|
904
|
-
positionBound(item, channel) {
|
|
905
|
-
let enc = this.getEncodingByItem(item, channel);
|
|
906
|
-
if (enc)
|
|
907
|
-
return enc;
|
|
908
|
-
else if (isPath(item)) {
|
|
909
|
-
for (let key in this._encodings) {
|
|
910
|
-
let classId = key.split("_")[0];
|
|
911
|
-
if (classId === item.classId && channel in this._encodings[key])
|
|
912
|
-
return this._encodings[key][channel];
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
else
|
|
916
|
-
return null;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
sizeBound(mark, channel) {
|
|
920
|
-
let enc = this.getEncodingByItem(mark, channel);
|
|
921
|
-
if (enc)
|
|
922
|
-
return enc;
|
|
923
|
-
if (isPath(mark)) {
|
|
924
|
-
let dep = channel === "width" ? ["area"] : channel === "height" ? ["area"] : channel === "radius" ? ["area"] : ["width", "height"],
|
|
925
|
-
dep2 = channel === "width" ? ["x"] : channel === "height" ? ["y"] : ["x", "y"];
|
|
926
|
-
for (let key in this._encodings) {
|
|
927
|
-
let classId = key.split("_")[0];
|
|
928
|
-
if (key.indexOf("_") < 0) {
|
|
929
|
-
for (let c of dep) {
|
|
930
|
-
if (classId === mark.classId && c in this._encodings[key])
|
|
931
|
-
return this._encodings[key][c];
|
|
932
|
-
}
|
|
933
|
-
} else {
|
|
934
|
-
for (let c of dep2) {
|
|
935
|
-
if (classId === mark.classId && c in this._encodings[key])
|
|
936
|
-
return this._encodings[key][c];
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
return null;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
setProperties(item, args) {
|
|
945
|
-
let result = {}, peers;
|
|
946
|
-
for (let p in args)
|
|
947
|
-
result[p] = true;
|
|
948
|
-
|
|
949
|
-
if (Object.values(LayoutType).indexOf(item.type) > -1 && item.group) {
|
|
950
|
-
peers = getPeers(item.group, this);
|
|
951
|
-
for (let coll of peers) {
|
|
952
|
-
for (let p in args) {
|
|
953
|
-
coll.layout[p] = args[p];
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
} else {
|
|
957
|
-
peers = getPeers(item, this);
|
|
958
|
-
if (item.type === "vertex") {
|
|
959
|
-
//TODO: validate the property names in args
|
|
960
|
-
for (let vertex of peers) {
|
|
961
|
-
for (let p in args) {
|
|
962
|
-
vertex[p] = args[p];
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
} else if (item instanceof Mark) {
|
|
966
|
-
for (let p in args) {
|
|
967
|
-
//TODO: validate p is a legit property, check if p is bound by data
|
|
968
|
-
if ((p === "x" || p === "y") && this.positionBound(item, p)) {
|
|
969
|
-
result[p] = false;
|
|
970
|
-
continue;
|
|
971
|
-
}
|
|
972
|
-
if ((p === "width" || p === "height" || p === "radius") && this.sizeBound(item, p)) {
|
|
973
|
-
//result[p] = false;
|
|
974
|
-
let enc = this.getEncodingByItem(item, p);
|
|
975
|
-
if (!enc && p == "radius") enc = this.getEncodingByItem(item, "area");
|
|
976
|
-
let re = enc.scale.rangeExtent * args[p] / item[p];
|
|
977
|
-
enc.scale.rangeExtent = re;
|
|
978
|
-
continue;
|
|
979
|
-
}
|
|
980
|
-
if ((item.type === ItemType.Rect || item.type === ItemType.Line) && (p === "width" || p === "height")) {
|
|
981
|
-
if (p === "width")
|
|
982
|
-
peers.forEach(d => d.resize(args[p], d.bounds.height, args["xRef"]));
|
|
983
|
-
else
|
|
984
|
-
peers.forEach(d => d.resize(d.bounds.width, args[p], undefined, args["yRef"]));
|
|
985
|
-
} else if (item.type == ItemType.Circle && p == "radius") {
|
|
986
|
-
peers.forEach(d => d.resize(args[p] * 2, args[p] * 2, args["xRef"], args["yRef"]));
|
|
987
|
-
} else {
|
|
988
|
-
peers.forEach(d => d[p] = args[p]);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
} else if (item.type == "collection" || item.type == "group") {
|
|
993
|
-
for (let c of peers) {
|
|
994
|
-
for (let p in args) {
|
|
995
|
-
c[p] = (p === "layout" && args[p] !== undefined) ? args[p].clone() : args[p];
|
|
996
|
-
}
|
|
997
|
-
//c._updateBounds();
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
//invoke SceneValidator accordingly
|
|
1003
|
-
let props = Object.keys(result).filter(d => result[d]);
|
|
1004
|
-
let sizeProps = ["width", "height", "fontSize", "area", "radius"];
|
|
1005
|
-
for (let s of sizeProps) {
|
|
1006
|
-
if (props.indexOf(s) >= 0 && isMark(item)) {
|
|
1007
|
-
SceneValidator.itemResized(item, peers);
|
|
1008
|
-
break;
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (props.indexOf("baseline") >= 0 && item.type === ItemType.Area)
|
|
1013
|
-
SceneValidator.areaRebased(item, peers);
|
|
1014
|
-
|
|
1015
|
-
let layoutProps = ["layout", "rowGap", "colGap", "numRows", "numCols", "orientation", "vertCellAlignment", "horzCellAlignment", "direction", "childrenOrder"];
|
|
1016
|
-
let changed = props.filter(d => layoutProps.indexOf(d) >= 0);
|
|
1017
|
-
if (changed.length > 0)
|
|
1018
|
-
SceneValidator.layoutChanged(peers[0], peers, changed);
|
|
1019
|
-
|
|
1020
|
-
return result;
|
|
1021
|
-
//TODO: relayout if needed (typically Layout or setProperty should happen before encoding)
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
_canTranslate(item) {
|
|
1025
|
-
let r = { x: true, y: true };
|
|
1026
|
-
if (item.type == ItemType.Axis) {
|
|
1027
|
-
if (item.channel === "x" || item.channel === "width")
|
|
1028
|
-
r.x = false;
|
|
1029
|
-
else if (item.channel === "y" || item.channel === "height")
|
|
1030
|
-
r.y = false;
|
|
1031
|
-
return r;
|
|
1032
|
-
}
|
|
1033
|
-
if (item.parent.type === ItemType.Collection && item.parent.layout) {
|
|
1034
|
-
r.x = false;
|
|
1035
|
-
r.y = false;
|
|
1036
|
-
}
|
|
1037
|
-
if (this.positionBound(item, "x"))
|
|
1038
|
-
r.x = false;
|
|
1039
|
-
if (this.positionBound(item, "y"))
|
|
1040
|
-
r.y = false;
|
|
1041
|
-
//TODO: check constraints, handle vertices and
|
|
1042
|
-
return r;
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
//always check canTranslate before calling this method
|
|
1046
|
-
translate(item, dx, dy) {
|
|
1047
|
-
let ct = this._canTranslate(item);
|
|
1048
|
-
if (!ct.x && !ct.y) return ct;
|
|
1049
|
-
const adx = ct.x ? dx : 0, ady = ct.y ? dy : 0;
|
|
1050
|
-
item._doTranslate(adx, ady);
|
|
1051
|
-
this._updateAncestorBounds(item);
|
|
1052
|
-
SceneValidator.itemTranslated(item, adx, ady);
|
|
1053
|
-
return ct;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
_updateAncestorBounds(cpnt, cpntPeers) {
|
|
1057
|
-
let peers = cpntPeers ? cpntPeers : getPeers(cpnt, this);
|
|
1058
|
-
let parents = getParents(peers);
|
|
1059
|
-
|
|
1060
|
-
while (parents.length > 0) {
|
|
1061
|
-
for (let p of parents) {
|
|
1062
|
-
if (p.children && p.children.length > 0) {
|
|
1063
|
-
p._updateBounds();
|
|
1064
|
-
} else if (p.vertices) {
|
|
1065
|
-
p._updateBounds();
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
parents = getParents(parents);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
** should only be used in Scene methods
|
|
1074
|
-
**/
|
|
1075
|
-
|
|
1076
|
-
_reapplyConstraints(item) {
|
|
1077
|
-
let itm = item, itms = [item];
|
|
1078
|
-
while (itm.children) {
|
|
1079
|
-
if (itm.type == ItemType.Collection) {
|
|
1080
|
-
itms.push(itm.firstChild);
|
|
1081
|
-
} else { //glyph or group
|
|
1082
|
-
itms = itms.concat(itm.children);
|
|
1083
|
-
}
|
|
1084
|
-
itm = itm.firstChild;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
itm = item.parent;
|
|
1088
|
-
while (itm && itm.type !== ItemType.Scene) {
|
|
1089
|
-
itms.push(itm);
|
|
1090
|
-
itm = itm.parent;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const classIds = itms.map(d => d.classId);
|
|
1094
|
-
|
|
1095
|
-
for (let c in this.constraints) {
|
|
1096
|
-
const cstr = this.constraints[c];
|
|
1097
|
-
switch (cstr.type) {
|
|
1098
|
-
case ConstraintType.Affix:
|
|
1099
|
-
if (classIds.indexOf(cstr.item.classId) >= 0 || classIds.indexOf(cstr.baseItem.classId) >= 0)
|
|
1100
|
-
cstr.apply();
|
|
1101
|
-
break;
|
|
1102
|
-
case ConstraintType.Align:
|
|
1103
|
-
for (let itm of cstr.items) {
|
|
1104
|
-
if (classIds.indexOf(itm.classId) >= 0) {
|
|
1105
|
-
cstr.apply();
|
|
1106
|
-
break;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
break;
|
|
1110
|
-
default:
|
|
1111
|
-
break;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
_relayoutAncestors(cpnt, cpntPeers) {
|
|
1117
|
-
let peers = cpntPeers ? cpntPeers : getPeers(cpnt, this);
|
|
1118
|
-
let parents = getParents(peers);
|
|
1119
|
-
while (parents.length > 0) {
|
|
1120
|
-
for (let p of parents) {
|
|
1121
|
-
if (p.layout) {
|
|
1122
|
-
p.layout.run();
|
|
1123
|
-
}
|
|
1124
|
-
if (p.children && p.children.length > 0) {
|
|
1125
|
-
p._updateBounds();
|
|
1126
|
-
}
|
|
1127
|
-
if (p.vertices) {
|
|
1128
|
-
p._updateBounds();
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
parents = getParents(parents);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
_reapplySizeBindings(compnt) {
|
|
1136
|
-
let sizeChannels = ["width", "height"];
|
|
1137
|
-
for (let classId in this._encodings) {
|
|
1138
|
-
if (compnt.classId != classId) continue;
|
|
1139
|
-
let encodings = this._encodings[classId];
|
|
1140
|
-
//TODO: re-use bindSpec and adjust scale accordingly
|
|
1141
|
-
let peers = findItems(this, [{ "classId": classId }]);
|
|
1142
|
-
for (let channel of sizeChannels) {
|
|
1143
|
-
let encoding = encodings[channel];
|
|
1144
|
-
if (!encoding) continue;
|
|
1145
|
-
encoding.run();
|
|
1146
|
-
}
|
|
1147
|
-
this._relayoutAncestors(peers[0], peers);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
_registerBinding(enc) {
|
|
1152
|
-
let encodings = this._encodings;
|
|
1153
|
-
let key = getEncodingKey(enc.anyItem);
|
|
1154
|
-
if (!(key in encodings))
|
|
1155
|
-
encodings[key] = {};
|
|
1156
|
-
encodings[key][enc.channel] = enc;
|
|
1157
|
-
return true;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
toJSON() {
|
|
1161
|
-
let json = super.toJSON();
|
|
1162
|
-
if (this.fillColor)
|
|
1163
|
-
json.fillColor = this.fillColor;
|
|
1164
|
-
let scales = {};
|
|
1165
|
-
json.encodings = [];
|
|
1166
|
-
json.itemCounter = ItemCounter;
|
|
1167
|
-
for (let classId in this._encodings) {
|
|
1168
|
-
for (let channel in this._encodings[classId]) {
|
|
1169
|
-
let e = this._encodings[classId][channel];
|
|
1170
|
-
json.encodings.push(e.toJSON());
|
|
1171
|
-
if (e.scale && !(e.scale.id in scales)) {
|
|
1172
|
-
scales[e.scale.id] = e.scale.toJSON();
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
json.scales = scales;
|
|
1177
|
-
json.constraints = {};
|
|
1178
|
-
for (let c in this.constraints)
|
|
1179
|
-
json.constraints[c] = this.constraints[c].toJSON();
|
|
1180
|
-
json.tables = {};
|
|
1181
|
-
let tables = this.getDataTables();
|
|
1182
|
-
for (let t in tables) {
|
|
1183
|
-
json.tables[t] = tables[t].toJSON();
|
|
1184
|
-
}
|
|
1185
|
-
return json;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
//get data tables used in this scene's encodings
|
|
1189
|
-
getDataTables() {
|
|
1190
|
-
let tables = {};
|
|
1191
|
-
for (let classId in this._encodings) {
|
|
1192
|
-
for (let channel in this._encodings[classId]) {
|
|
1193
|
-
let e = this._encodings[classId][channel];
|
|
1194
|
-
if (!(e.datatable.id in tables)) {
|
|
1195
|
-
tables[e.datatable.id] = e.datatable;
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
for (let c of this.children) {
|
|
1200
|
-
if (isGuide(c)) continue;
|
|
1201
|
-
if (c.dataScope) {
|
|
1202
|
-
tables[c.dataScope.dataTable.id] = c.dataScope.dataTable;
|
|
1203
|
-
break;
|
|
1204
|
-
} else if (c.children && c.children.length > 0) {
|
|
1205
|
-
let itm = c.firstChild;
|
|
1206
|
-
while (itm) {
|
|
1207
|
-
if (itm.dataScope) {
|
|
1208
|
-
tables[itm.dataScope.dataTable.id] = itm.dataScope.dataTable;
|
|
1209
|
-
break;
|
|
1210
|
-
} else if (itm.children && itm.children.length > 0) {
|
|
1211
|
-
itm = itm.firstChild
|
|
1212
|
-
} else {
|
|
1213
|
-
itm = undefined;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
return tables;
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
//handles marks, groups, vertices and segments
|
|
1222
|
-
getItem(id) {
|
|
1223
|
-
let markId = id.split("_")[0];
|
|
1224
|
-
if (id.indexOf("_v_") > 0) {
|
|
1225
|
-
let idx = parseInt(id.split("_v_")[1]);
|
|
1226
|
-
return this._itemMap[markId].vertices.find(d => d._id === idx);
|
|
1227
|
-
} else if (id.indexOf("_s_") > 0) {
|
|
1228
|
-
let idx = parseInt(id.split("_s_")[1]);
|
|
1229
|
-
return this._itemMap[markId].segments.find(d => d._id === idx);
|
|
1230
|
-
} else {
|
|
1231
|
-
return this._itemMap[id];
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
addInteraction(obj) {
|
|
1236
|
-
// let i = new Interaction(listener, eventType, selDef, targetDef);
|
|
1237
|
-
this._interactions.push(obj);
|
|
1238
|
-
this._interactionsObj[obj['event']] = obj;
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
get encodings() {
|
|
1242
|
-
let r = [];
|
|
1243
|
-
for (let itmKey in this._encodings) {
|
|
1244
|
-
let enc = this._encodings[itmKey];
|
|
1245
|
-
for (let channel in enc) {
|
|
1246
|
-
r.push(enc[channel]);
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
return r;
|
|
1250
|
-
}
|
|
1251
|
-
}
|