| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- class GraphPreviewer {
- constructor(p_vxcore, p_container) {
- this.vxcore = p_vxcore;
- // Preview will take place here.
- this.container = p_container;
- this.flowchartJsIdx = 0;
- this.waveDromIdx = 0;
- this.mermaidIdx = 0;
- // Used to decide the width with 100% relative value.
- this.windowWidth = 800;
- this.firstPreview = true;
- this.currentColor = null;
- window.addEventListener(
- 'resize',
- () => {
- if (window.innerWidth > 0) {
- this.windowWidth = window.innerWidth;
- }
- },
- { passive: true });
- }
- // Interface 1.
- previewGraph(p_id, p_timeStamp, p_lang, p_text) {
- if (p_text.length == 0) {
- this.setGraphPreviewData(p_id, p_timeStamp);
- return;
- }
- this.initOnFirstPreview();
- if (p_lang === 'flow' || p_lang === 'flowchart') {
- this.vxcore.getWorker('flowchartjs').renderText(this.container,
- p_text,
- this.flowchartJsIdx++,
- (graphDiv) => {
- this.processGraph(p_id, p_timeStamp, graphDiv);
- });
- } else if (p_lang === 'wavedrom') {
- this.vxcore.getWorker('wavedrom').renderText(this.container,
- p_text,
- this.waveDromIdx++,
- (graphDiv) => {
- this.processGraph(p_id, p_timeStamp, graphDiv);
- });
- } else if (p_lang === 'mermaid') {
- this.vxcore.getWorker('mermaid').renderText(this.container,
- p_text,
- this.mermaidIdx++,
- (graphDiv) => {
- this.fixSvgRelativeWidth(graphDiv.firstElementChild);
- this.processGraph(p_id, p_timeStamp, graphDiv);
- });
- } else if (p_lang === 'puml' || p_lang === 'plantuml') {
- let func = function(p_previewer, p_id, p_timeStamp) {
- let previewer = p_previewer;
- let id = p_id;
- let timeStamp = p_timeStamp;
- return function(p_format, p_data) {
- previewer.setGraphPreviewData(id, timeStamp, p_format, p_data, false, true);
- };
- };
- this.vxcore.getWorker('plantuml').renderText(p_text, func(this, p_id, p_timeStamp));
- return;
- } else if (p_lang === 'dot' || p_lang === 'graphviz') {
- let func = function(p_previewer, p_id, p_timeStamp) {
- let previewer = p_previewer;
- let id = p_id;
- let timeStamp = p_timeStamp;
- return function(p_svgNode) {
- previewer.setGraphPreviewData(id, timeStamp, 'svg', p_svgNode.outerHTML, false, true);
- };
- };
- this.vxcore.getWorker('graphviz').renderText(p_text, func(this, p_id, p_timeStamp));
- return;
- } else if (p_lang === 'mathjax') {
- this.renderMath(p_id, p_timeStamp, p_text, null);
- return;
- } else {
- this.setGraphPreviewData(p_id, p_timeStamp);
- }
- }
- // Interface 2.
- previewMath(p_id, p_timeStamp, p_text) {
- if (p_text.length == 0) {
- this.setMathPreviewData(p_id, p_timeStamp);
- return;
- }
- this.initOnFirstPreview();
- // Do we need to go through TexMath plugin? I don't think so.
- this.renderMath(p_id, p_timeStamp, p_text, this.setMathPreviewData.bind(this));
- }
- initOnFirstPreview() {
- if (this.firstPreview) {
- this.firstPreview = false;
- let contentStyle = window.getComputedStyle(this.vxcore.contentContainer);
- this.currentColor = contentStyle.getPropertyValue('color');
- console.log('currentColor', this.currentColor);
- }
- }
- renderMath(p_id, p_timeStamp, p_text, p_dataSetter) {
- let func = function(p_previewer, p_id, p_timeStamp) {
- let previewer = p_previewer;
- let id = p_id;
- let timeStamp = p_timeStamp;
- return function(p_svgNode) {
- previewer.fixSvgCurrentColor(p_svgNode);
- previewer.fixSvgRelativeWidth(p_svgNode);
- previewer.processSvgAsPng(id, timeStamp, p_svgNode, p_dataSetter);
- };
- };
- this.vxcore.getWorker('mathjax').renderText(this.container,
- p_text,
- func(this, p_id, p_timeStamp));
- }
- processGraph(p_id, p_timeStamp, p_graphDiv) {
- if (!p_graphDiv) {
- console.error('failed to preview graph', p_id, p_timeStamp);
- this.setGraphPreviewData(p_id, p_timeStamp);
- return;
- }
- this.container.removeChild(p_graphDiv);
- this.processSvgAsPng(p_id, p_timeStamp, p_graphDiv.firstElementChild);
- }
- processSvgAsPng(p_id, p_timeStamp, p_svgNode, p_dataSetter = null) {
- if (!p_dataSetter) {
- p_dataSetter = this.setGraphPreviewData.bind(this);
- }
- if (!p_svgNode) {
- console.warn('failed to preview graph', p_id, p_timeStamp);
- p_dataSetter(p_id, p_timeStamp);
- return;
- }
- this.scaleSvg(p_svgNode);
- SvgToImage.svgToImage(p_svgNode.outerHTML,
- { crossOrigin: 'Anonymous' },
- (p_err, p_image) => {
- if (p_err) {
- p_dataSetter(p_id, p_timeStamp);
- return;
- }
- let canvas = document.createElement('canvas');
- let ctx = canvas.getContext('2d');
- canvas.height = p_image.height;
- canvas.width = p_image.width;
- ctx.drawImage(p_image, 0, 0);
- let dataUrl = null;
- try {
- dataUrl = canvas.toDataURL();
- } catch (err) {
- // Tainted canvas may be caused by the <foreignObject> in SVG.
- console.error('failed to draw image on canvas', err);
- // Try simply using the SVG.
- p_dataSetter(p_id, p_timeStamp, 'svg', p_svgNode.outerHTML, false, false);
- return;
- }
- let png = dataUrl ? dataUrl.substring(dataUrl.indexOf(',') + 1) : '';
- p_dataSetter(p_id, p_timeStamp, 'png', png, true, false);
- });
- }
- // Fix SVG with width and height being '100%'.
- fixSvgRelativeWidth(p_svgNode) {
- if (!p_svgNode) {
- return;
- }
- if (p_svgNode.getAttribute('width').indexOf('%') != -1) {
- // Try maxWidth.
- if (p_svgNode.style.maxWidth && p_svgNode.style.maxWidth.endsWith('px')) {
- p_svgNode.setAttribute('width', p_svgNode.style.maxWidth);
- } else {
- // Set as window width.
- p_svgNode.setAttribute('width', Math.max(this.windowWidth - 100, 100) + 'px');
- }
- }
- }
- // Fix SVG with stroke="currentColor" and fill="currentColor".
- fixSvgCurrentColor(p_svgNode) {
- let currentColor = this.currentColor;
- if (currentColor && p_svgNode) {
- let nodes = p_svgNode.querySelectorAll("g[fill='currentColor']");
- for (let i = 0; i < nodes.length; ++i) {
- let node = nodes[i];
- if (node.getAttribute('stroke') === 'currentColor') {
- node.setAttribute('stroke', currentColor);
- }
- if (node.getAttribute('fill') === 'currentColor') {
- node.setAttribute('fill', currentColor);
- }
- }
- }
- }
- scaleSvg(p_svgNode) {
- let scaleFactor = window.devicePixelRatio;
- if (scaleFactor == 1 || !p_svgNode) {
- return;
- }
- if (p_svgNode.getAttribute('width').indexOf('%') == -1) {
- p_svgNode.width.baseVal.valueInSpecifiedUnits *= scaleFactor;
- }
- if (p_svgNode.getAttribute('height').indexOf('%') == -1) {
- p_svgNode.height.baseVal.valueInSpecifiedUnits *= scaleFactor;
- }
- }
- setGraphPreviewData(p_id, p_timeStamp, p_format = '', p_data = '', p_base64 = false, p_needScale = false) {
- let previewData = {
- id: p_id,
- timeStamp: p_timeStamp,
- format: p_format,
- data: p_data,
- base64: p_base64,
- needScale: p_needScale
- };
- this.vxcore.setGraphPreviewData(previewData);
- }
- setMathPreviewData(p_id, p_timeStamp, p_format = '', p_data = '', p_base64 = false, p_needScale = false) {
- let previewData = {
- id: p_id,
- timeStamp: p_timeStamp,
- format: p_format,
- data: p_data,
- base64: p_base64,
- needScale: p_needScale
- };
- this.vxcore.setMathPreviewData(previewData);
- }
- }
|