UNPKG

26 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var fp_1 = require("lodash/fp");
4var RGBColor = require("rgbcolor");
5var VError = require("verror");
6var he_1 = require("he");
7var gpml_utilities_1 = require("../gpml-utilities");
8var Angle_1 = require("../spinoffs/Angle");
9var 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?
16var 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};
22function decodeIfNotEmpty(input) {
23 return fp_1.isEmpty(input) ? input : he_1.decode(input);
24}
25function 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
40function 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}
49exports.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".
54exports.$ID = fp_1.flow(fp_1.get("$ID"), gpml_utilities_1.generatePublicationXrefId);
55exports.DB = fp_1.flow(fp_1.get("DB.content"), decodeIfNotEmpty);
56exports.TITLE = fp_1.flow(fp_1.get("TITLE.content"), decodeIfNotEmpty);
57exports.SOURCE = fp_1.flow(fp_1.get("SOURCE.content"), decodeIfNotEmpty);
58exports.YEAR = fp_1.get("YEAR.content");
59exports.AUTHORS = fp_1.flow(fp_1.get("AUTHORS"), fp_1.map(fp_1.flow(fp_1.get("content"), decodeIfNotEmpty)));
60exports.BiopaxRef = fp_1.flow(fp_1.get("BiopaxRef"), fp_1.map(gpml_utilities_1.generatePublicationXrefId));
61/*
62Meanings of Width
63-----------------
64
65In PathVisio-Java, GPML Width/Height for GPML Shapes is
66inconsistent when zoomed in vs. when at default zoom level.
67
68When zoomed in, GPML Width/Height refers to the distance from center of stroke (border)
69one one edge to center of stroke (border) on the opposite edge, meaning that shapes that
70run up to the edge are cropped.
71
72When at default zoom level, GPML Width/Height refers to the distance from outer edge of
73stroke (border) to outer edge of stroke (border) with no cropping.
74
75Because of this, LineThickness is also inconsistent.
76When zoomed in: approx. one half of specified LineThickness.
77When at default zoom level: approx. full specified LineThickness.
78
79For double lines, LineThickness refers to the the stroke (border) width of each line and
80the space between each line, meaning the stroke (border) width
81for the double line as a whole will be three times the listed LineThickness.
82
83For pvjs, we define GPML Width/Height to be from outer edge of stroke (border) on one
84side to outer edge of stroke (border) on the opposite site, meaning visible width/height
85may not exactly match between pvjs and PathVisio.
86See 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//*/
118var 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});
130exports.Height = getDimension("Height");
131exports.Width = getDimension("Width");
132function CenterX(gpmlElement) {
133 var CenterX = gpmlElement.Graphics.CenterX;
134 return CenterX - exports.Width(gpmlElement) / 2;
135}
136exports.CenterX = CenterX;
137function CenterY(gpmlElement) {
138 var CenterY = gpmlElement.Graphics.CenterY;
139 return CenterY - exports.Height(gpmlElement) / 2;
140}
141exports.CenterY = CenterY;
142function 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}
167exports.Rotation = Rotation;
168function 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}
185exports.LineStyle = LineStyle;
186exports.Author = fp_1.flow(fp_1.get("Author"), decodeIfNotEmpty);
187exports.DataSource = fp_1.flow(fp_1.get("Data-Source"), decodeIfNotEmpty);
188exports.Email = fp_1.flow(fp_1.get("Email"), decodeIfNotEmpty);
189exports.Maintainer = fp_1.flow(fp_1.get("Maintainer"), decodeIfNotEmpty);
190exports.Name = fp_1.flow(fp_1.get("Name"), decodeIfNotEmpty);
191exports.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.
196function 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}
214exports.getTextDecorationFromGPMLElement = getTextDecorationFromGPMLElement;
215exports.Align = fp_1.flow(fp_1.get("Graphics.Align"), fp_1.kebabCase);
216exports.FontDecoration = getTextDecorationFromGPMLElement;
217exports.FontStrikethru = getTextDecorationFromGPMLElement;
218exports.FontStyle = fp_1.flow(fp_1.get("Graphics.FontStyle"), fp_1.kebabCase);
219exports.FontWeight = fp_1.flow(fp_1.get("Graphics.FontWeight"), fp_1.kebabCase);
220exports.Valign = fp_1.flow(fp_1.get("Graphics.Valign"), fp_1.kebabCase);
221exports.Href = fp_1.flow(fp_1.get("Href"), decodeIfNotEmpty, encodeURI);
222function 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}
237exports.gpmlColorToCssColor = gpmlColorToCssColor;
238exports.Color = fp_1.flow(fp_1.get("Graphics.Color"), gpmlColorToCssColor);
239function 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}
250exports.FillColor = FillColor;
251function 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}
282exports.LineThickness = LineThickness;
283function ConnectorType(gpmlElement) {
284 var ConnectorType = gpmlElement.Graphics.ConnectorType;
285 return ConnectorType + "Line";
286}
287exports.ConnectorType = ConnectorType;
288// We return a partial attachmentDisplay, because it's
289// merged with the other items as we come across them.
290function 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}
298exports.Position = Position;
299/**
300 * getPositionAndRelativeOffsetScalarsAlongAxis
301 *
302 * @param relValue {number}
303 * @return {OffsetOrientationAndPositionScalarsAlongAxis}
304 */
305function 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.
333function 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}
349exports.RelX = RelX;
350//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVmFsdWVDb252ZXJ0ZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjLzIwMTNhL1ZhbHVlQ29udmVydGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLGdDQVVtQjtBQUNuQixJQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDckMsK0JBQWlDO0FBQ2pDLHlCQUE0QjtBQUU1QixvREFJMkI7QUFDM0IsMkNBQWdFO0FBQ2hFLG9EQUFrRDtBQUdsRCwyQ0FBMkM7QUFDM0Msd0NBQXdDO0FBQ3hDLGtDQUFrQztBQUNsQywwQ0FBMEM7QUFDMUMsNkNBQTZDO0FBQzdDLGdCQUFnQjtBQUNoQixJQUFNLHlCQUF5QixHQUFHO0lBQ2hDLEdBQUcsRUFBRSxDQUFDO0lBQ04sS0FBSyxFQUFFLEdBQUcsR0FBRyxJQUFJLENBQUMsRUFBRTtJQUNwQixNQUFNLEVBQUUsSUFBSSxDQUFDLEVBQUU7SUFDZixJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRTtDQUN0QixDQUFDO0FBRUYsMEJBQTBCLEtBQUs7SUFDN0IsTUFBTSxDQUFDLFlBQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxXQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDaEQsQ0FBQztBQUVELDZCQUE2QixDQUFrQjtJQUM3QyxJQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekIsRUFBRSxDQUFDLENBQUMsVUFBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsR0FBRyxxQkFBcUIsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxtQkFBbUI7QUFDbkIsbUJB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
\No newline at end of file