1 | /**
|
2 | * The code in the <project-logo></project-logo> area
|
3 | * must not be changed.
|
4 | *
|
5 | * @see http://bpmn.io/license for more information.
|
6 | */
|
7 | import {
|
8 | assign,
|
9 | find,
|
10 | isNumber,
|
11 | omit
|
12 | } from 'min-dash';
|
13 |
|
14 | import {
|
15 | domify,
|
16 | assignStyle,
|
17 | query as domQuery,
|
18 | remove as domRemove
|
19 | } from 'min-dom';
|
20 |
|
21 | import {
|
22 | innerSVG
|
23 | } from 'tiny-svg';
|
24 |
|
25 | import Diagram from 'diagram-js';
|
26 | import BpmnModdle from 'bpmn-moddle';
|
27 |
|
28 | import inherits from 'inherits-browser';
|
29 |
|
30 | import {
|
31 | importBpmnDiagram
|
32 | } from './import/Importer';
|
33 |
|
34 | /**
|
35 | * @template T
|
36 | *
|
37 | * @typedef {import('diagram-js/lib/core/EventBus').EventBusEventCallback<T>} EventBusEventCallback
|
38 | */
|
39 |
|
40 | /**
|
41 | * @typedef {import('didi').ModuleDeclaration} ModuleDeclaration
|
42 | *
|
43 | * @typedef {import('./model/Types').Moddle} Moddle
|
44 | * @typedef {import('./model/Types').ModdleElement} ModdleElement
|
45 | * @typedef {import('./model/Types').ModdleExtension} ModdleExtension
|
46 | *
|
47 | * @typedef { {
|
48 | * width?: number|string;
|
49 | * height?: number|string;
|
50 | * position?: string;
|
51 | * container?: string|HTMLElement;
|
52 | * moddleExtensions?: ModdleExtensions;
|
53 | * additionalModules?: ModuleDeclaration[];
|
54 | * } & Record<string, any> } BaseViewerOptions
|
55 | *
|
56 | * @typedef {Record<string, ModdleElement>} ModdleElementsById
|
57 | *
|
58 | * @typedef { {
|
59 | * [key: string]: ModdleExtension;
|
60 | * } } ModdleExtensions
|
61 | *
|
62 | * @typedef { {
|
63 | * warnings: string[];
|
64 | * } } ImportXMLResult
|
65 | *
|
66 | * @typedef {ImportXMLResult & Error} ImportXMLError
|
67 | *
|
68 | * @typedef {ImportXMLResult} ImportDefinitionsResult
|
69 | *
|
70 | * @typedef {ImportXMLError} ImportDefinitionsError
|
71 | *
|
72 | * @typedef {ImportXMLResult} OpenResult
|
73 | *
|
74 | * @typedef {ImportXMLError} OpenError
|
75 | *
|
76 | * @typedef { {
|
77 | * format?: boolean;
|
78 | * preamble?: boolean;
|
79 | * } } SaveXMLOptions
|
80 | *
|
81 | * @typedef { {
|
82 | * xml?: string;
|
83 | * error?: Error;
|
84 | * } } SaveXMLResult
|
85 | *
|
86 | * @typedef { {
|
87 | * svg: string;
|
88 | * } } SaveSVGResult
|
89 | *
|
90 | * @typedef { {
|
91 | * xml: string;
|
92 | * } } ImportParseStartEvent
|
93 | *
|
94 | * @typedef { {
|
95 | * error?: ImportXMLError;
|
96 | * definitions?: ModdleElement;
|
97 | * elementsById?: ModdleElementsById;
|
98 | * references?: ModdleElement[];
|
99 | * warnings: string[];
|
100 | * } } ImportParseCompleteEvent
|
101 | *
|
102 | * @typedef { {
|
103 | * error?: ImportXMLError;
|
104 | * warnings: string[];
|
105 | * } } ImportDoneEvent
|
106 | *
|
107 | * @typedef { {
|
108 | * definitions: ModdleElement;
|
109 | * } } SaveXMLStartEvent
|
110 | *
|
111 | * @typedef {SaveXMLResult} SaveXMLDoneEvent
|
112 | *
|
113 | * @typedef { {
|
114 | * error?: Error;
|
115 | * svg: string;
|
116 | * } } SaveSVGDoneEvent
|
117 | */
|
118 |
|
119 | /**
|
120 | * A base viewer for BPMN 2.0 diagrams.
|
121 | *
|
122 | * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for
|
123 | * bundles that include actual features.
|
124 | *
|
125 | * @param {BaseViewerOptions} [options] The options to configure the viewer.
|
126 | */
|
127 | export default function BaseViewer(options) {
|
128 |
|
129 | /**
|
130 | * @type {BaseViewerOptions}
|
131 | */
|
132 | options = assign({}, DEFAULT_OPTIONS, options);
|
133 |
|
134 | /**
|
135 | * @type {Moddle}
|
136 | */
|
137 | this._moddle = this._createModdle(options);
|
138 |
|
139 | /**
|
140 | * @type {HTMLElement}
|
141 | */
|
142 | this._container = this._createContainer(options);
|
143 |
|
144 | /* <project-logo> */
|
145 |
|
146 | addProjectLogo(this._container);
|
147 |
|
148 | /* </project-logo> */
|
149 |
|
150 | this._init(this._container, this._moddle, options);
|
151 | }
|
152 |
|
153 | inherits(BaseViewer, Diagram);
|
154 |
|
155 | /**
|
156 | * Parse and render a BPMN 2.0 diagram.
|
157 | *
|
158 | * Once finished the viewer reports back the result to the
|
159 | * provided callback function with (err, warnings).
|
160 | *
|
161 | * ## Life-Cycle Events
|
162 | *
|
163 | * During import the viewer will fire life-cycle events:
|
164 | *
|
165 | * * import.parse.start (about to read model from XML)
|
166 | * * import.parse.complete (model read; may have worked or not)
|
167 | * * import.render.start (graphical import start)
|
168 | * * import.render.complete (graphical import finished)
|
169 | * * import.done (everything done)
|
170 | *
|
171 | * You can use these events to hook into the life-cycle.
|
172 | *
|
173 | * @throws {ImportXMLError} An error thrown during the import of the XML.
|
174 | *
|
175 | * @fires BaseViewer#ImportParseStartEvent
|
176 | * @fires BaseViewer#ImportParseCompleteEvent
|
177 | * @fires Importer#ImportRenderStartEvent
|
178 | * @fires Importer#ImportRenderCompleteEvent
|
179 | * @fires BaseViewer#ImportDoneEvent
|
180 | *
|
181 | * @param {string} xml The BPMN 2.0 XML to be imported.
|
182 | * @param {ModdleElement|string} [bpmnDiagram] The optional diagram or Id of the BPMN diagram to open.
|
183 | *
|
184 | * @return {Promise<ImportXMLResult>} A promise resolving with warnings that were produced during the import.
|
185 | */
|
186 | BaseViewer.prototype.importXML = async function importXML(xml, bpmnDiagram) {
|
187 |
|
188 | const self = this;
|
189 |
|
190 | function ParseCompleteEvent(data) {
|
191 | return self.get('eventBus').createEvent(data);
|
192 | }
|
193 |
|
194 | let aggregatedWarnings = [];
|
195 | try {
|
196 |
|
197 | // hook in pre-parse listeners +
|
198 | // allow xml manipulation
|
199 |
|
200 | /**
|
201 | * A `import.parse.start` event.
|
202 | *
|
203 | * @event BaseViewer#ImportParseStartEvent
|
204 | * @type {ImportParseStartEvent}
|
205 | */
|
206 | xml = this._emit('import.parse.start', { xml: xml }) || xml;
|
207 |
|
208 | let parseResult;
|
209 | try {
|
210 | parseResult = await this._moddle.fromXML(xml, 'bpmn:Definitions');
|
211 | } catch (error) {
|
212 | this._emit('import.parse.complete', {
|
213 | error
|
214 | });
|
215 |
|
216 | throw error;
|
217 | }
|
218 |
|
219 | let definitions = parseResult.rootElement;
|
220 | const references = parseResult.references;
|
221 | const parseWarnings = parseResult.warnings;
|
222 | const elementsById = parseResult.elementsById;
|
223 |
|
224 | aggregatedWarnings = aggregatedWarnings.concat(parseWarnings);
|
225 |
|
226 | // hook in post parse listeners +
|
227 | // allow definitions manipulation
|
228 |
|
229 | /**
|
230 | * A `import.parse.complete` event.
|
231 | *
|
232 | * @event BaseViewer#ImportParseCompleteEvent
|
233 | * @type {ImportParseCompleteEvent}
|
234 | */
|
235 | definitions = this._emit('import.parse.complete', ParseCompleteEvent({
|
236 | error: null,
|
237 | definitions: definitions,
|
238 | elementsById: elementsById,
|
239 | references: references,
|
240 | warnings: aggregatedWarnings
|
241 | })) || definitions;
|
242 |
|
243 | const importResult = await this.importDefinitions(definitions, bpmnDiagram);
|
244 |
|
245 | aggregatedWarnings = aggregatedWarnings.concat(importResult.warnings);
|
246 |
|
247 | /**
|
248 | * A `import.parse.complete` event.
|
249 | *
|
250 | * @event BaseViewer#ImportDoneEvent
|
251 | * @type {ImportDoneEvent}
|
252 | */
|
253 | this._emit('import.done', { error: null, warnings: aggregatedWarnings });
|
254 |
|
255 | return { warnings: aggregatedWarnings };
|
256 | } catch (err) {
|
257 | let error = err;
|
258 | aggregatedWarnings = aggregatedWarnings.concat(error.warnings || []);
|
259 | addWarningsToError(error, aggregatedWarnings);
|
260 |
|
261 | error = checkValidationError(error);
|
262 |
|
263 | this._emit('import.done', { error, warnings: error.warnings });
|
264 |
|
265 | throw error;
|
266 | }
|
267 | };
|
268 |
|
269 |
|
270 | /**
|
271 | * Import parsed definitions and render a BPMN 2.0 diagram.
|
272 | *
|
273 | * Once finished the viewer reports back the result to the
|
274 | * provided callback function with (err, warnings).
|
275 | *
|
276 | * ## Life-Cycle Events
|
277 | *
|
278 | * During import the viewer will fire life-cycle events:
|
279 | *
|
280 | * * import.render.start (graphical import start)
|
281 | * * import.render.complete (graphical import finished)
|
282 | *
|
283 | * You can use these events to hook into the life-cycle.
|
284 | *
|
285 | * @throws {ImportDefinitionsError} An error thrown during the import of the definitions.
|
286 | *
|
287 | * @param {ModdleElement} definitions The definitions.
|
288 | * @param {ModdleElement|string} [bpmnDiagram] The optional diagram or ID of the BPMN diagram to open.
|
289 | *
|
290 | * @return {Promise<ImportDefinitionsResult>} A promise resolving with warnings that were produced during the import.
|
291 | */
|
292 | BaseViewer.prototype.importDefinitions = async function importDefinitions(definitions, bpmnDiagram) {
|
293 | this._setDefinitions(definitions);
|
294 | const result = await this.open(bpmnDiagram);
|
295 |
|
296 | return { warnings: result.warnings };
|
297 | };
|
298 |
|
299 |
|
300 | /**
|
301 | * Open diagram of previously imported XML.
|
302 | *
|
303 | * Once finished the viewer reports back the result to the
|
304 | * provided callback function with (err, warnings).
|
305 | *
|
306 | * ## Life-Cycle Events
|
307 | *
|
308 | * During switch the viewer will fire life-cycle events:
|
309 | *
|
310 | * * import.render.start (graphical import start)
|
311 | * * import.render.complete (graphical import finished)
|
312 | *
|
313 | * You can use these events to hook into the life-cycle.
|
314 | *
|
315 | * @throws {OpenError} An error thrown during opening.
|
316 | *
|
317 | * @param {ModdleElement|string} bpmnDiagramOrId The diagram or Id of the BPMN diagram to open.
|
318 | *
|
319 | * @return {Promise<OpenResult>} A promise resolving with warnings that were produced during opening.
|
320 | */
|
321 | BaseViewer.prototype.open = async function open(bpmnDiagramOrId) {
|
322 |
|
323 | const definitions = this._definitions;
|
324 | let bpmnDiagram = bpmnDiagramOrId;
|
325 |
|
326 | if (!definitions) {
|
327 | const error = new Error('no XML imported');
|
328 | addWarningsToError(error, []);
|
329 |
|
330 | throw error;
|
331 | }
|
332 |
|
333 | if (typeof bpmnDiagramOrId === 'string') {
|
334 | bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId);
|
335 |
|
336 | if (!bpmnDiagram) {
|
337 | const error = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found');
|
338 | addWarningsToError(error, []);
|
339 |
|
340 | throw error;
|
341 | }
|
342 | }
|
343 |
|
344 | // clear existing rendered diagram
|
345 | // catch synchronous exceptions during #clear()
|
346 | try {
|
347 | this.clear();
|
348 | } catch (error) {
|
349 | addWarningsToError(error, []);
|
350 |
|
351 | throw error;
|
352 | }
|
353 |
|
354 | // perform graphical import
|
355 | const { warnings } = await importBpmnDiagram(this, definitions, bpmnDiagram);
|
356 |
|
357 | return { warnings };
|
358 | };
|
359 |
|
360 | /**
|
361 | * Export the currently displayed BPMN 2.0 diagram as
|
362 | * a BPMN 2.0 XML document.
|
363 | *
|
364 | * ## Life-Cycle Events
|
365 | *
|
366 | * During XML saving the viewer will fire life-cycle events:
|
367 | *
|
368 | * * saveXML.start (before serialization)
|
369 | * * saveXML.serialized (after xml generation)
|
370 | * * saveXML.done (everything done)
|
371 | *
|
372 | * You can use these events to hook into the life-cycle.
|
373 | *
|
374 | * @throws {Error} An error thrown during export.
|
375 | *
|
376 | * @fires BaseViewer#SaveXMLStart
|
377 | * @fires BaseViewer#SaveXMLDone
|
378 | *
|
379 | * @param {SaveXMLOptions} [options] The options.
|
380 | *
|
381 | * @return {Promise<SaveXMLResult>} A promise resolving with the XML.
|
382 | */
|
383 | BaseViewer.prototype.saveXML = async function saveXML(options) {
|
384 |
|
385 | options = options || {};
|
386 |
|
387 | let definitions = this._definitions,
|
388 | error, xml;
|
389 |
|
390 | try {
|
391 | if (!definitions) {
|
392 | throw new Error('no definitions loaded');
|
393 | }
|
394 |
|
395 | // allow to fiddle around with definitions
|
396 |
|
397 | /**
|
398 | * A `saveXML.start` event.
|
399 | *
|
400 | * @event BaseViewer#SaveXMLStartEvent
|
401 | * @type {SaveXMLStartEvent}
|
402 | */
|
403 | definitions = this._emit('saveXML.start', {
|
404 | definitions
|
405 | }) || definitions;
|
406 |
|
407 | const result = await this._moddle.toXML(definitions, options);
|
408 | xml = result.xml;
|
409 |
|
410 | xml = this._emit('saveXML.serialized', {
|
411 | xml
|
412 | }) || xml;
|
413 | } catch (err) {
|
414 | error = err;
|
415 | }
|
416 |
|
417 | const result = error ? { error } : { xml };
|
418 |
|
419 | /**
|
420 | * A `saveXML.done` event.
|
421 | *
|
422 | * @event BaseViewer#SaveXMLDoneEvent
|
423 | * @type {SaveXMLDoneEvent}
|
424 | */
|
425 | this._emit('saveXML.done', result);
|
426 |
|
427 | if (error) {
|
428 | throw error;
|
429 | }
|
430 |
|
431 | return result;
|
432 | };
|
433 |
|
434 |
|
435 | /**
|
436 | * Export the currently displayed BPMN 2.0 diagram as
|
437 | * an SVG image.
|
438 | *
|
439 | * ## Life-Cycle Events
|
440 | *
|
441 | * During SVG saving the viewer will fire life-cycle events:
|
442 | *
|
443 | * * saveSVG.start (before serialization)
|
444 | * * saveSVG.done (everything done)
|
445 | *
|
446 | * You can use these events to hook into the life-cycle.
|
447 | *
|
448 | * @throws {Error} An error thrown during export.
|
449 | *
|
450 | * @fires BaseViewer#SaveSVGDone
|
451 | *
|
452 | * @return {Promise<SaveSVGResult>} A promise resolving with the SVG.
|
453 | */
|
454 | BaseViewer.prototype.saveSVG = async function saveSVG() {
|
455 | this._emit('saveSVG.start');
|
456 |
|
457 | let svg, err;
|
458 |
|
459 | try {
|
460 | const canvas = this.get('canvas');
|
461 |
|
462 | const contentNode = canvas.getActiveLayer(),
|
463 | defsNode = domQuery('defs', canvas._svg);
|
464 |
|
465 | const contents = innerSVG(contentNode),
|
466 | defs = defsNode ? '<defs>' + innerSVG(defsNode) + '</defs>' : '';
|
467 |
|
468 | const bbox = contentNode.getBBox();
|
469 |
|
470 | svg =
|
471 | '<?xml version="1.0" encoding="utf-8"?>\n' +
|
472 | '<!-- created with bpmn-js / http://bpmn.io -->\n' +
|
473 | '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
|
474 | '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
|
475 | 'width="' + bbox.width + '" height="' + bbox.height + '" ' +
|
476 | 'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' +
|
477 | defs + contents +
|
478 | '</svg>';
|
479 | } catch (e) {
|
480 | err = e;
|
481 | }
|
482 |
|
483 | /**
|
484 | * A `saveSVG.done` event.
|
485 | *
|
486 | * @event BaseViewer#SaveSVGDoneEvent
|
487 | * @type {SaveSVGDoneEvent}
|
488 | */
|
489 | this._emit('saveSVG.done', {
|
490 | error: err,
|
491 | svg: svg
|
492 | });
|
493 |
|
494 | if (err) {
|
495 | throw err;
|
496 | }
|
497 |
|
498 | return { svg };
|
499 | };
|
500 |
|
501 | BaseViewer.prototype._setDefinitions = function(definitions) {
|
502 | this._definitions = definitions;
|
503 | };
|
504 |
|
505 | /**
|
506 | * Return modules to instantiate with.
|
507 | *
|
508 | * @return {ModuleDeclaration[]} The modules.
|
509 | */
|
510 | BaseViewer.prototype.getModules = function() {
|
511 | return this._modules;
|
512 | };
|
513 |
|
514 | /**
|
515 | * Remove all drawn elements from the viewer.
|
516 | *
|
517 | * After calling this method the viewer can still be reused for opening another
|
518 | * diagram.
|
519 | */
|
520 | BaseViewer.prototype.clear = function() {
|
521 | if (!this.getDefinitions()) {
|
522 |
|
523 | // no diagram to clear
|
524 | return;
|
525 | }
|
526 |
|
527 | // remove drawn elements
|
528 | Diagram.prototype.clear.call(this);
|
529 | };
|
530 |
|
531 | /**
|
532 | * Destroy the viewer instance and remove all its remainders from the document
|
533 | * tree.
|
534 | */
|
535 | BaseViewer.prototype.destroy = function() {
|
536 |
|
537 | // diagram destroy
|
538 | Diagram.prototype.destroy.call(this);
|
539 |
|
540 | // dom detach
|
541 | domRemove(this._container);
|
542 | };
|
543 |
|
544 | /**
|
545 | * Register an event listener.
|
546 | *
|
547 | * Remove an event listener via {@link BaseViewer#off}.
|
548 | *
|
549 | * @template T
|
550 | *
|
551 | * @param {string|string[]} events The event(s) to listen to.
|
552 | * @param {number} [priority] The priority with which to listen.
|
553 | * @param {EventBusEventCallback<T>} callback The callback.
|
554 | * @param {any} [that] Value of `this` the callback will be called with.
|
555 | */
|
556 | BaseViewer.prototype.on = function(events, priority, callback, that) {
|
557 | return this.get('eventBus').on(events, priority, callback, that);
|
558 | };
|
559 |
|
560 | /**
|
561 | * Remove an event listener.
|
562 | *
|
563 | * @param {string|string[]} events The event(s).
|
564 | * @param {Function} [callback] The callback.
|
565 | */
|
566 | BaseViewer.prototype.off = function(events, callback) {
|
567 | this.get('eventBus').off(events, callback);
|
568 | };
|
569 |
|
570 | /**
|
571 | * Attach the viewer to an HTML element.
|
572 | *
|
573 | * @param {HTMLElement} parentNode The parent node to attach to.
|
574 | */
|
575 | BaseViewer.prototype.attachTo = function(parentNode) {
|
576 |
|
577 | if (!parentNode) {
|
578 | throw new Error('parentNode required');
|
579 | }
|
580 |
|
581 | // ensure we detach from the
|
582 | // previous, old parent
|
583 | this.detach();
|
584 |
|
585 | // unwrap jQuery if provided
|
586 | if (parentNode.get && parentNode.constructor.prototype.jquery) {
|
587 | parentNode = parentNode.get(0);
|
588 | }
|
589 |
|
590 | if (typeof parentNode === 'string') {
|
591 | parentNode = domQuery(parentNode);
|
592 | }
|
593 |
|
594 | parentNode.appendChild(this._container);
|
595 |
|
596 | this._emit('attach', {});
|
597 |
|
598 | this.get('canvas').resized();
|
599 | };
|
600 |
|
601 | /**
|
602 | * Get the definitions model element.
|
603 | *
|
604 | * @return {ModdleElement} The definitions model element.
|
605 | */
|
606 | BaseViewer.prototype.getDefinitions = function() {
|
607 | return this._definitions;
|
608 | };
|
609 |
|
610 | /**
|
611 | * Detach the viewer.
|
612 | *
|
613 | * @fires BaseViewer#DetachEvent
|
614 | */
|
615 | BaseViewer.prototype.detach = function() {
|
616 |
|
617 | const container = this._container,
|
618 | parentNode = container.parentNode;
|
619 |
|
620 | if (!parentNode) {
|
621 | return;
|
622 | }
|
623 |
|
624 | /**
|
625 | * A `detach` event.
|
626 | *
|
627 | * @event BaseViewer#DetachEvent
|
628 | * @type {Object}
|
629 | */
|
630 | this._emit('detach', {});
|
631 |
|
632 | parentNode.removeChild(container);
|
633 | };
|
634 |
|
635 | BaseViewer.prototype._init = function(container, moddle, options) {
|
636 |
|
637 | const baseModules = options.modules || this.getModules(options),
|
638 | additionalModules = options.additionalModules || [],
|
639 | staticModules = [
|
640 | {
|
641 | bpmnjs: [ 'value', this ],
|
642 | moddle: [ 'value', moddle ]
|
643 | }
|
644 | ];
|
645 |
|
646 | const diagramModules = [].concat(staticModules, baseModules, additionalModules);
|
647 |
|
648 | const diagramOptions = assign(omit(options, [ 'additionalModules' ]), {
|
649 | canvas: assign({}, options.canvas, { container: container }),
|
650 | modules: diagramModules
|
651 | });
|
652 |
|
653 | // invoke diagram constructor
|
654 | Diagram.call(this, diagramOptions);
|
655 |
|
656 | if (options && options.container) {
|
657 | this.attachTo(options.container);
|
658 | }
|
659 | };
|
660 |
|
661 | /**
|
662 | * Emit an event on the underlying {@link EventBus}
|
663 | *
|
664 | * @param {string} type
|
665 | * @param {Object} event
|
666 | *
|
667 | * @return {Object} The return value after calling all event listeners.
|
668 | */
|
669 | BaseViewer.prototype._emit = function(type, event) {
|
670 | return this.get('eventBus').fire(type, event);
|
671 | };
|
672 |
|
673 | /**
|
674 | * @param {BaseViewerOptions} options
|
675 | *
|
676 | * @return {HTMLElement}
|
677 | */
|
678 | BaseViewer.prototype._createContainer = function(options) {
|
679 |
|
680 | const container = domify('<div class="bjs-container"></div>');
|
681 |
|
682 | assignStyle(container, {
|
683 | width: ensureUnit(options.width),
|
684 | height: ensureUnit(options.height),
|
685 | position: options.position
|
686 | });
|
687 |
|
688 | return container;
|
689 | };
|
690 |
|
691 | /**
|
692 | * @param {BaseViewerOptions} options
|
693 | *
|
694 | * @return {Moddle}
|
695 | */
|
696 | BaseViewer.prototype._createModdle = function(options) {
|
697 | const moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
|
698 |
|
699 | return new BpmnModdle(moddleOptions);
|
700 | };
|
701 |
|
702 | BaseViewer.prototype._modules = [];
|
703 |
|
704 | // helpers ///////////////
|
705 |
|
706 | function addWarningsToError(err, warningsAry) {
|
707 | err.warnings = warningsAry;
|
708 | return err;
|
709 | }
|
710 |
|
711 | function checkValidationError(err) {
|
712 |
|
713 | // check if we can help the user by indicating wrong BPMN 2.0 xml
|
714 | // (in case he or the exporting tool did not get that right)
|
715 |
|
716 | const pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
|
717 | const match = pattern.exec(err.message);
|
718 |
|
719 | if (match) {
|
720 | err.message =
|
721 | 'unparsable content <' + match[1] + '> detected; ' +
|
722 | 'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
|
723 | }
|
724 |
|
725 | return err;
|
726 | }
|
727 |
|
728 | const DEFAULT_OPTIONS = {
|
729 | width: '100%',
|
730 | height: '100%',
|
731 | position: 'relative'
|
732 | };
|
733 |
|
734 |
|
735 | /**
|
736 | * Ensure the passed argument is a proper unit (defaulting to px)
|
737 | */
|
738 | function ensureUnit(val) {
|
739 | return val + (isNumber(val) ? 'px' : '');
|
740 | }
|
741 |
|
742 |
|
743 | /**
|
744 | * Find BPMNDiagram in definitions by ID
|
745 | *
|
746 | * @param {ModdleElement<Definitions>} definitions
|
747 | * @param {string} diagramId
|
748 | *
|
749 | * @return {ModdleElement<BPMNDiagram>|null}
|
750 | */
|
751 | function findBPMNDiagram(definitions, diagramId) {
|
752 | if (!diagramId) {
|
753 | return null;
|
754 | }
|
755 |
|
756 | return find(definitions.diagrams, function(element) {
|
757 | return element.id === diagramId;
|
758 | }) || null;
|
759 | }
|
760 |
|
761 |
|
762 | /* <project-logo> */
|
763 |
|
764 | import {
|
765 | open as openPoweredBy,
|
766 | BPMNIO_IMG,
|
767 | LOGO_STYLES,
|
768 | LINK_STYLES
|
769 | } from './util/PoweredByUtil';
|
770 |
|
771 | import {
|
772 | event as domEvent
|
773 | } from 'min-dom';
|
774 |
|
775 | /**
|
776 | * Adds the project logo to the diagram container as
|
777 | * required by the bpmn.io license.
|
778 | *
|
779 | * @see http://bpmn.io/license
|
780 | *
|
781 | * @param {Element} container
|
782 | */
|
783 | function addProjectLogo(container) {
|
784 | const img = BPMNIO_IMG;
|
785 |
|
786 | const linkMarkup =
|
787 | '<a href="http://bpmn.io" ' +
|
788 | 'target="_blank" ' +
|
789 | 'class="bjs-powered-by" ' +
|
790 | 'title="Powered by bpmn.io" ' +
|
791 | '>' +
|
792 | img +
|
793 | '</a>';
|
794 |
|
795 | const linkElement = domify(linkMarkup);
|
796 |
|
797 | assignStyle(domQuery('svg', linkElement), LOGO_STYLES);
|
798 | assignStyle(linkElement, LINK_STYLES, {
|
799 | position: 'absolute',
|
800 | bottom: '15px',
|
801 | right: '15px',
|
802 | zIndex: '100'
|
803 | });
|
804 |
|
805 | container.appendChild(linkElement);
|
806 |
|
807 | domEvent.bind(linkElement, 'click', function(event) {
|
808 | openPoweredBy();
|
809 |
|
810 | event.preventDefault();
|
811 | });
|
812 | }
|
813 |
|
814 | /* </project-logo> */
|