1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var fp_1 = require("lodash/fp");
|
4 | var RGBColor = require("rgbcolor");
|
5 | var VError = require("verror");
|
6 | var he_1 = require("he");
|
7 | var gpml_utilities_1 = require("../gpml-utilities");
|
8 | var Angle_1 = require("../spinoffs/Angle");
|
9 | var gpml_utilities_2 = require("../gpml-utilities");
|
10 | // TODO are these ever used? PathVisio-Java
|
11 | // does not accept them as inputs in the
|
12 | // Rotation input field in the UI.
|
13 | // TODO if they are used, are the notes in
|
14 | // the XSD correct, or would "Right" actually
|
15 | // be 0 radians?
|
16 | var GPML_ROTATION_SIDE_TO_RAD = {
|
17 | Top: 0,
|
18 | Right: 0.5 * Math.PI,
|
19 | Bottom: Math.PI,
|
20 | Left: 3 / 2 * Math.PI
|
21 | };
|
22 | function decodeIfNotEmpty(input) {
|
23 | return fp_1.isEmpty(input) ? input : he_1.decode(input);
|
24 | }
|
25 | function parseAsNonNaNNumber(i) {
|
26 | var parsed = Number(i);
|
27 | if (fp_1.isNaN(parsed)) {
|
28 | throw new Error('Cannot parse "' + String(i) + '" as non-NaN number');
|
29 | }
|
30 | return parsed;
|
31 | }
|
32 | //*****************
|
33 | // Value Converters
|
34 | //*****************
|
35 | // NOTE: we use He.decode for many of these
|
36 | // because at some point some GPML files were
|
37 | // processed w/out using UTF-8, leaving some
|
38 | // strings garbled, such as author names.
|
39 | // TODO backpageHead could be further processed to yield displayName and standardName
|
40 | function ID(gpmlElement) {
|
41 | if (gpmlElement.hasOwnProperty("ID")) {
|
42 | var ID_1 = gpmlElement.ID;
|
43 | return fp_1.isString(ID_1) ? ID_1 : ID_1.content;
|
44 | }
|
45 | else {
|
46 | return gpmlElement.Xref.ID;
|
47 | }
|
48 | }
|
49 | exports.ID = ID;
|
50 | // GPML2013-ish incorrectly used "rdf:id" where it was intented
|
51 | // to use "rdf:ID". We corrected that error before processing,
|
52 | // but CXML turns "rdf:ID" into "ID", and since we already have
|
53 | // a property "ID" on the element, CXML uses "$ID".
|
54 | exports.$ID = fp_1.flow(fp_1.get("$ID"), gpml_utilities_1.generatePublicationXrefId);
|
55 | exports.DB = fp_1.flow(fp_1.get("DB.content"), decodeIfNotEmpty);
|
56 | exports.TITLE = fp_1.flow(fp_1.get("TITLE.content"), decodeIfNotEmpty);
|
57 | exports.SOURCE = fp_1.flow(fp_1.get("SOURCE.content"), decodeIfNotEmpty);
|
58 | exports.YEAR = fp_1.get("YEAR.content");
|
59 | exports.AUTHORS = fp_1.flow(fp_1.get("AUTHORS"), fp_1.map(fp_1.flow(fp_1.get("content"), decodeIfNotEmpty)));
|
60 | exports.BiopaxRef = fp_1.flow(fp_1.get("BiopaxRef"), fp_1.map(gpml_utilities_1.generatePublicationXrefId));
|
61 | /*
|
62 | Meanings of Width
|
63 | -----------------
|
64 |
|
65 | In PathVisio-Java, GPML Width/Height for GPML Shapes is
|
66 | inconsistent when zoomed in vs. when at default zoom level.
|
67 |
|
68 | When zoomed in, GPML Width/Height refers to the distance from center of stroke (border)
|
69 | one one edge to center of stroke (border) on the opposite edge, meaning that shapes that
|
70 | run up to the edge are cropped.
|
71 |
|
72 | When at default zoom level, GPML Width/Height refers to the distance from outer edge of
|
73 | stroke (border) to outer edge of stroke (border) with no cropping.
|
74 |
|
75 | Because of this, LineThickness is also inconsistent.
|
76 | When zoomed in: approx. one half of specified LineThickness.
|
77 | When at default zoom level: approx. full specified LineThickness.
|
78 |
|
79 | For double lines, LineThickness refers to the the stroke (border) width of each line and
|
80 | the space between each line, meaning the stroke (border) width
|
81 | for the double line as a whole will be three times the listed LineThickness.
|
82 |
|
83 | For pvjs, we define GPML Width/Height to be from outer edge of stroke (border) on one
|
84 | side to outer edge of stroke (border) on the opposite site, meaning visible width/height
|
85 | may not exactly match between pvjs and PathVisio.
|
86 | See issue https://github.com/PathVisio/pathvisio/issues/59
|
87 |
|
88 | * DOM box model
|
89 | - box-sizing: border-box
|
90 | visible width = width
|
91 | (width means border + padding + width of the content)
|
92 | (see https://css-tricks.com/international-box-sizing-awareness-day/)
|
93 | - box-sizing: content-box
|
94 | visible width = width + border + padding
|
95 | (width means width of the content)
|
96 | * PathVisio-Java
|
97 | - Zoomed in
|
98 | - LineStyle NOT Double
|
99 | visible width ≈ GPMLWidth
|
100 | visible height ≈ GPMLHeight
|
101 | (matches box-sizing: border-box)
|
102 | - LineStyle Double
|
103 | visible width ≈ Width + 1.5 * LineThickness
|
104 | visible height ≈ Height + 1.5 * LineThickness
|
105 | - Zoomed out
|
106 | - LineStyle NOT Double
|
107 | visible width ≈ GPMLWidth + LineThickness
|
108 | visible height ≈ GPMLHeight + LineThickness
|
109 | (matches box-sizing: border-box)
|
110 | (one half LineThickness on either side yields a full LineThickness to add
|
111 | to width/height).
|
112 | - LineStyle Double
|
113 | visible width = Width + 3 * LineThickness
|
114 | visible height = Height + 3 * LineThickness
|
115 | * SVG: visible width = width + stroke-width
|
116 | * kaavio/pvjs: same as DOM box model with box-sizing: border-box
|
117 | //*/
|
118 | var getDimension = fp_1.curry(function (dimensionName, gpmlElement) {
|
119 | var dimension = gpmlElement.Graphics[dimensionName];
|
120 | if (fp_1.findIndex(function (_a) {
|
121 | var Key = _a.Key, Value = _a.Value;
|
122 | return Key === "org.pathvisio.DoubleLineProperty";
|
123 | }, gpmlElement.Attribute) > -1) {
|
124 | return dimension + LineThickness(gpmlElement);
|
125 | }
|
126 | else {
|
127 | return dimension;
|
128 | }
|
129 | });
|
130 | exports.Height = getDimension("Height");
|
131 | exports.Width = getDimension("Width");
|
132 | function CenterX(gpmlElement) {
|
133 | var CenterX = gpmlElement.Graphics.CenterX;
|
134 | return CenterX - exports.Width(gpmlElement) / 2;
|
135 | }
|
136 | exports.CenterX = CenterX;
|
137 | function CenterY(gpmlElement) {
|
138 | var CenterY = gpmlElement.Graphics.CenterY;
|
139 | return CenterY - exports.Height(gpmlElement) / 2;
|
140 | }
|
141 | exports.CenterY = CenterY;
|
142 | function Rotation(gpmlElement) {
|
143 | // NOTE: the rotation input field in the PathVisio-Java UI expects degrees,
|
144 | // but GPML expresses rotation in radians. The XSD indicates GPML can also
|
145 | // use directional strings, although I haven't seen one used in actual GPML.
|
146 | // For both the PathVisio-Java UI and GPML, a positive value means clockwise
|
147 | // rotation.
|
148 | // NOTE: GPML can hold a rotation value for State elements in an element
|
149 | // named "Attribute" like this:
|
150 | // Key="org.pathvisio.core.StateRotation"
|
151 | // From discussion with AP and KH, we've decided to ignore this value,
|
152 | // because we don't actually want States to be rotated.
|
153 | var Graphics = gpmlElement.Graphics;
|
154 | var Rotation = !gpml_utilities_2.isDefinedCXML(Graphics.Rotation) ? 0 : Graphics.Rotation;
|
155 | // NOTE: Output is in degrees, because that's what the SVG transform
|
156 | // attribute accepts. Don't get confused, because we use radians in
|
157 | // the edge processing.
|
158 | //
|
159 | // NOTE: to make it as simple as possible for users to work with pvjson,
|
160 | // we're normalizing these rotation values so they are always positive values
|
161 | // between 0 and 2 * Math.PI, e.g.,
|
162 | // (3/2) * Math.PI, not -1 * Math.PI/2 or (7/3) * Math.PI
|
163 | return Angle_1.radiansToDegrees(Angle_1.normalize(GPML_ROTATION_SIDE_TO_RAD.hasOwnProperty(Rotation)
|
164 | ? GPML_ROTATION_SIDE_TO_RAD[Rotation]
|
165 | : parseAsNonNaNNumber(Rotation)));
|
166 | }
|
167 | exports.Rotation = Rotation;
|
168 | function LineStyle(gpmlElement) {
|
169 | var LineStyle = gpmlElement.Graphics.LineStyle;
|
170 | // TODO hard-coding this here is not the most maintainable
|
171 | if (LineStyle === "Solid") {
|
172 | // this gets converted to strokeDasharray,
|
173 | // and we don't need this value when it's
|
174 | // solid, so we return undefined, because
|
175 | // then this won't be included.
|
176 | return;
|
177 | }
|
178 | else if (LineStyle === "Broken") {
|
179 | return "5,3";
|
180 | }
|
181 | else {
|
182 | throw new Error("Unrecognized LineStyle: " + LineStyle);
|
183 | }
|
184 | }
|
185 | exports.LineStyle = LineStyle;
|
186 | exports.Author = fp_1.flow(fp_1.get("Author"), decodeIfNotEmpty);
|
187 | exports.DataSource = fp_1.flow(fp_1.get("Data-Source"), decodeIfNotEmpty);
|
188 | exports.Email = fp_1.flow(fp_1.get("Email"), decodeIfNotEmpty);
|
189 | exports.Maintainer = fp_1.flow(fp_1.get("Maintainer"), decodeIfNotEmpty);
|
190 | exports.Name = fp_1.flow(fp_1.get("Name"), decodeIfNotEmpty);
|
191 | exports.TextLabel = fp_1.flow(fp_1.get("TextLabel"), decodeIfNotEmpty);
|
192 | // TODO is this ever used?
|
193 | // The only way I see to create underlined text in PathVisio-Java
|
194 | // is to create a Label and fill in the Link field.
|
195 | // But the resulting GPML does not have a FontDecoration attribute.
|
196 | function getTextDecorationFromGPMLElement(gpmlElement) {
|
197 | var _a = gpmlElement.Graphics, FontDecoration = _a.FontDecoration, FontStrikethru = _a.FontStrikethru;
|
198 | var outputChunks = [];
|
199 | var fontDecorationDefined = gpml_utilities_2.isDefinedCXML(FontDecoration) && FontDecoration === "Underline";
|
200 | var fontStrikethruDefined = gpml_utilities_2.isDefinedCXML(FontStrikethru) && FontStrikethru === "Strikethru";
|
201 | if (fontDecorationDefined || fontStrikethruDefined) {
|
202 | if (fontDecorationDefined) {
|
203 | outputChunks.push("underline");
|
204 | }
|
205 | if (fontStrikethruDefined) {
|
206 | outputChunks.push("line-through");
|
207 | }
|
208 | }
|
209 | else {
|
210 | outputChunks.push("none");
|
211 | }
|
212 | return outputChunks.join(" ");
|
213 | }
|
214 | exports.getTextDecorationFromGPMLElement = getTextDecorationFromGPMLElement;
|
215 | exports.Align = fp_1.flow(fp_1.get("Graphics.Align"), fp_1.kebabCase);
|
216 | exports.FontDecoration = getTextDecorationFromGPMLElement;
|
217 | exports.FontStrikethru = getTextDecorationFromGPMLElement;
|
218 | exports.FontStyle = fp_1.flow(fp_1.get("Graphics.FontStyle"), fp_1.kebabCase);
|
219 | exports.FontWeight = fp_1.flow(fp_1.get("Graphics.FontWeight"), fp_1.kebabCase);
|
220 | exports.Valign = fp_1.flow(fp_1.get("Graphics.Valign"), fp_1.kebabCase);
|
221 | exports.Href = fp_1.flow(fp_1.get("Href"), decodeIfNotEmpty, encodeURI);
|
222 | function gpmlColorToCssColor(colorValue) {
|
223 | var colorValueLowerCased = colorValue.toLowerCase();
|
224 | if (["transparent", "none"].indexOf(colorValueLowerCased) > -1) {
|
225 | return colorValueLowerCased;
|
226 | }
|
227 | else {
|
228 | var color = new RGBColor(colorValue);
|
229 | if (!color.ok) {
|
230 | throw new VError("\n\t\t\t\tFailed to get a valid CSS color for gpmlColorToCssColor(" + colorValue + ")\n\t\t\t\tIs there an invalid Color or FillColor in the GPML?\n\t\t\t\t");
|
231 | // TODO should we use this?
|
232 | // return "#c0c0c0";
|
233 | }
|
234 | return color.toHex();
|
235 | }
|
236 | }
|
237 | exports.gpmlColorToCssColor = gpmlColorToCssColor;
|
238 | exports.Color = fp_1.flow(fp_1.get("Graphics.Color"), gpmlColorToCssColor);
|
239 | function FillColor(gpmlElement) {
|
240 | var _a = gpmlElement.Graphics, FillColor = _a.FillColor, ShapeType = _a.ShapeType;
|
241 | // If it's a GPML Group, DataNode, Shape, Label or State, it needs a
|
242 | // ShapeType in order for it to have a FillColor, but a
|
243 | // GPML Interaction or GraphicalLine can have a FillColor
|
244 | // without having a ShapeType.
|
245 | return (!!ShapeType && ShapeType.toLowerCase() !== "none") ||
|
246 | gpmlElement.Graphics.hasOwnProperty("Point")
|
247 | ? gpmlColorToCssColor(FillColor)
|
248 | : "transparent";
|
249 | }
|
250 | exports.FillColor = FillColor;
|
251 | function LineThickness(gpmlElement) {
|
252 | var _a = gpmlElement.Graphics, LineThickness = _a.LineThickness, ShapeType = _a.ShapeType;
|
253 | // See note near Height converter regarding LineThickness.
|
254 | // If it's a GPML Group, DataNode, Shape, Label or State, it needs a
|
255 | // ShapeType in order for it to have a LineThickness > 0, but a
|
256 | // GPML Interaction or GraphicalLine can have a LineThickness > 0
|
257 | // without having a ShapeType.
|
258 | if (!gpml_utilities_2.isDefinedCXML(LineThickness)) {
|
259 | return 0;
|
260 | }
|
261 | else if (gpml_utilities_2.isDefinedCXML(ShapeType) && ShapeType.toLowerCase() !== "none") {
|
262 | /*
|
263 | return findIndex(function({ Key, Value }) {
|
264 | return Key === "org.pathvisio.DoubleLineProperty";
|
265 | }, gpmlElement.Attribute) > -1 ? LineThickness * 3 : LineThickness;
|
266 | //*/
|
267 | /*
|
268 | return findIndex(function({ Key, Value }) {
|
269 | return Key === "org.pathvisio.DoubleLineProperty";
|
270 | }, gpmlElement.Attribute) > -1 ? LineThickness : LineThickness * 2;
|
271 | //*/
|
272 | //return LineThickness * 2;
|
273 | return LineThickness;
|
274 | }
|
275 | else if (gpmlElement.Graphics.hasOwnProperty("Point")) {
|
276 | return LineThickness;
|
277 | }
|
278 | else {
|
279 | return 0;
|
280 | }
|
281 | }
|
282 | exports.LineThickness = LineThickness;
|
283 | function ConnectorType(gpmlElement) {
|
284 | var ConnectorType = gpmlElement.Graphics.ConnectorType;
|
285 | return ConnectorType + "Line";
|
286 | }
|
287 | exports.ConnectorType = ConnectorType;
|
288 | // We return a partial attachmentDisplay, because it's
|
289 | // merged with the other items as we come across them.
|
290 | function Position(gpmlElement) {
|
291 | var Position = gpmlElement.Position;
|
292 | return {
|
293 | position: [Position, 0],
|
294 | // a GPML Anchor never has an offset
|
295 | offset: [0, 0]
|
296 | };
|
297 | }
|
298 | exports.Position = Position;
|
299 | /**
|
300 | * getPositionAndRelativeOffsetScalarsAlongAxis
|
301 | *
|
302 | * @param relValue {number}
|
303 | * @return {OffsetOrientationAndPositionScalarsAlongAxis}
|
304 | */
|
305 | function getPositionAndRelativeOffsetScalarsAlongAxis(relValue) {
|
306 | var relativeOffsetScalar;
|
307 | var positionScalar;
|
308 | var relativeToUpperLeftCorner = (relValue + 1) / 2;
|
309 | if (relativeToUpperLeftCorner < 0 || relativeToUpperLeftCorner > 1) {
|
310 | if (relativeToUpperLeftCorner < 0) {
|
311 | positionScalar = 0;
|
312 | relativeOffsetScalar = relativeToUpperLeftCorner;
|
313 | }
|
314 | else {
|
315 | positionScalar = 1;
|
316 | relativeOffsetScalar = relativeToUpperLeftCorner - 1;
|
317 | }
|
318 | }
|
319 | else {
|
320 | positionScalar = relativeToUpperLeftCorner;
|
321 | relativeOffsetScalar = 0;
|
322 | }
|
323 | if (!isFinite(positionScalar) || !isFinite(relativeOffsetScalar)) {
|
324 | throw new Error("Expected finite values for positionScalar " + positionScalar + " and relativeOffsetScalar " + relativeOffsetScalar);
|
325 | }
|
326 | return { relativeOffsetScalar: relativeOffsetScalar, positionScalar: positionScalar };
|
327 | }
|
328 | // We actually handle both RelX and RelY together
|
329 | // when we hit RelX and then ignoring when we
|
330 | // hit RelY.
|
331 | // We return a partial attachmentDisplay, because it's
|
332 | // merged with the other items as we come across them.
|
333 | function RelX(gpmlElement) {
|
334 | // first is for a State (?), second is for a Point
|
335 | var RelXRelYContainer = gpml_utilities_2.isDefinedCXML(gpmlElement.Graphics)
|
336 | ? gpmlElement.Graphics
|
337 | : gpmlElement;
|
338 | var RelX = RelXRelYContainer.RelX, RelY = RelXRelYContainer.RelY;
|
339 | var _a = getPositionAndRelativeOffsetScalarsAlongAxis(RelX), relativeOffsetScalarX = _a.relativeOffsetScalar, positionScalarX = _a.positionScalar;
|
340 | var _b = getPositionAndRelativeOffsetScalarsAlongAxis(RelY), relativeOffsetScalarY = _b.relativeOffsetScalar, positionScalarY = _b.positionScalar;
|
341 | return {
|
342 | position: [positionScalarX, positionScalarY],
|
343 | // we can't calculate absolute offset until we get the
|
344 | // referenced element width/height
|
345 | offset: [],
|
346 | relativeOffset: [relativeOffsetScalarX, relativeOffsetScalarY]
|
347 | };
|
348 | }
|
349 | exports.RelX = RelX;
|
350 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVmFsdWVDb252ZXJ0ZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjLzIwMTNhL1ZhbHVlQ29udmVydGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLGdDQVVtQjtBQUNuQixJQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDckMsK0JBQWlDO0FBQ2pDLHlCQUE0QjtBQUU1QixvREFJMkI7QUFDM0IsMkNBQWdFO0FBQ2hFLG9EQUFrRDtBQUdsRCwyQ0FBMkM7QUFDM0Msd0NBQXdDO0FBQ3hDLGtDQUFrQztBQUNsQywwQ0FBMEM7QUFDMUMsNkNBQTZDO0FBQzdDLGdCQUFnQjtBQUNoQixJQUFNLHlCQUF5QixHQUFHO0lBQ2hDLEdBQUcsRUFBRSxDQUFDO0lBQ04sS0FBSyxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRTtJQUNwQixNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUU7SUFDZixJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRTtDQUN0QixDQUFDO0FBRUYsMEJBQTBCLEtBQUs7SUFDN0IsTUFBTSxDQUFDLFlBQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxXQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDaEQsQ0FBQztBQUVELDZCQUE2QixDQUFrQjtJQUM3QyxJQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekIsRUFBRSxDQUFDLENBQUMsVUFBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsR0FBRyxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxtQkFBbUI7QUFDbkIsbUJB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 |
\ | No newline at end of file |