Skip to content

Commit

Permalink
Merge pull request #388 from nus-mtp/dev/mermaidAPI-enhancement
Browse files Browse the repository at this point in the history
Refactor diagramsRenderer Component to improve rendering performance
  • Loading branch information
amoshydra committed Apr 15, 2017
2 parents 2c5e2e3 + 790a426 commit 8fa02ae
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 34 deletions.
3 changes: 0 additions & 3 deletions sashimi-webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@
<script src="./vendors/viz-lite.js"></script>
<!-- mermaid does not support markdown-it, hence we are doing this -->
<script src="./vendors/mermaidAPI.min.js"></script>
<!-- Customised mermaid.css taken from hackmd.io, this custom CSS resolves the issue of -->
<!-- the styling applying to the whole HTML document -->
<link href="./vendors/mermaid.css" rel="stylesheet">
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://code.jquery.com/pep/0.4.2/pep.js"></script>
Expand Down
15 changes: 7 additions & 8 deletions sashimi-webapp/src/components/editor-viewer/Viewers/Html.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,28 @@

<script>
import Vue from 'vue';
import diagramsRenderer from 'src/logic/renderer/diagrams';
import DiagramsRenderer from 'src/logic/renderer/diagrams';
import documentBuilder from 'src/helpers/documentBuilder';
/**
* Diagram rendering function for HTML view
* @param {element} renderTarget - HTML element that will be used to render data into
* @param {string} htmlData - string containing the parsed and rendered markdown syntax by markdown-it
*/
function renderUpdate(renderTarget, htmlData) {
renderTarget.innerHTML = htmlData;
// find everything and replace/drawsvg
diagramsRenderer(renderTarget);
}
export default {
props: ['htmlData'],
data() {
return {
diagramsRenderer: null,
renderDoc: null,
};
},
watch: {
htmlData(data) {
if (this.renderDoc) {
renderUpdate(this.renderDoc.body, data);
this.renderDoc.body.innerHTML = this.htmlData;
this.diagramsRenderer.process(this.renderDoc.body);
}
}
},
Expand All @@ -55,7 +52,9 @@
})
.then(() => {
this.renderDoc = this.$el.contentWindow.document;
renderUpdate(this.renderDoc.body, this.htmlData);
this.renderDoc.body.innerHTML = this.htmlData;
this.diagramsRenderer = new DiagramsRenderer();
this.diagramsRenderer.process(this.renderDoc.body);
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<script>
import Vue from 'vue';
import _ from 'lodash';
import DiagramsRenderer from 'src/logic/renderer/diagrams';
import PageRenderer from 'src/logic/renderer';
import DocumentNavigator from 'src/logic/inputHandler/DocumentNavigator';
import documentBuilder from 'src/helpers/documentBuilder';
Expand All @@ -23,6 +24,7 @@
props: ['htmlData'],
data() {
return {
diagramsRenderer: null,
pageRenderer: null,
documentNavigator: null,
pageSize: { // PAGE_A4
Expand All @@ -45,6 +47,7 @@
mounted() {
// Mount does not gurrantee DOM to be ready, thus nextTick is used
Vue.nextTick(() => {
this.diagramsRenderer = new DiagramsRenderer();
documentBuilder.rebuild(this.$el);
documentBuilder.addStyles(this.$el, [
'/styles/markdown-pages.css',
Expand All @@ -70,7 +73,9 @@
return eleContainer;
})
.then((renderTarget) => {
this.pageRenderer = new PageRenderer(renderTarget, this.pageSize);
this.pageRenderer = new PageRenderer(renderTarget, this.pageSize, [
this.diagramsRenderer
]);
return renderThrottleFn(this.htmlData, this.pageRenderer)
.then(() => {
// Initialise navigation for Pages mode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<script>
import Vue from 'vue';
import _ from 'lodash';
import DiagramsRenderer from 'src/logic/renderer/diagrams';
import PageRenderer from 'src/logic/renderer';
import DocumentNavigator from 'src/logic/inputHandler/DocumentNavigator';
import SlidesNavigator from 'src/logic/inputHandler/SlidesNavigator';
Expand All @@ -26,6 +27,7 @@
props: ['htmlData'],
data() {
return {
diagramsRenderer: null,
pageRenderer: null,
documentNavigator: null,
pageSize: { // PAGE_A6
Expand All @@ -48,6 +50,7 @@
mounted() {
// Mount does not gurrantee DOM to be ready, thus nextTick is used
Vue.nextTick(() => {
this.diagramsRenderer = new DiagramsRenderer();
documentBuilder.rebuild(this.$el);
documentBuilder.addStyles(this.$el, [
'/styles/markdown-html.css',
Expand All @@ -73,7 +76,9 @@
return eleContainer;
})
.then((renderTarget) => {
this.pageRenderer = new PageRenderer(renderTarget, this.pageSize);
this.pageRenderer = new PageRenderer(renderTarget, this.pageSize, [
this.diagramsRenderer
]);
return renderThrottleFn(this.htmlData, this.pageRenderer)
.then(() => {
// Initialise navigation for Slide mode
Expand Down
18 changes: 16 additions & 2 deletions sashimi-webapp/src/logic/inputHandler/DocumentPrinter/core.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import documentPackager from 'src/logic/documentPackager';
import documentBuilder from 'src/helpers/documentBuilder';
import DiagramsRenderer from 'src/logic/renderer/diagrams';
import PageRenderer from 'src/logic/renderer';

const frame = {
Expand All @@ -8,12 +9,23 @@ const frame = {
feature: 'location=yes,height=600,width=800,scrollbars=yes',
};

const pageSize = { // PAGE_A4
width: '21.0cm',
height: '29.7cm',
padding: {
top: '2.54cm',
bottom: '2.54cm',
right: '2.54cm',
left: '2.54cm'
}
};

export default {
print(markdownData) {
const printFrame = window.open(frame.url, frame.target, frame.feature);
documentBuilder.rebuild(printFrame);
documentBuilder.addStyles(printFrame, [
'/styles/markdown-html.css',
'/styles/markdown-pages.css',
'/styles/viewer-page.css',
'/styles/markdown-imports.css'
])
Expand All @@ -28,7 +40,9 @@ export default {
return eleContainer;
})
.then((renderTarget) => {
const pr = new PageRenderer(renderTarget);
const pr = new PageRenderer(renderTarget, pageSize, [
new DiagramsRenderer()
]);
return documentPackager.getHtmlData(markdownData)
.then(htmlData => pr.write(htmlData));
})
Expand Down
9 changes: 6 additions & 3 deletions sashimi-webapp/src/logic/renderer/core.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import diagramsRenderer from './diagrams';
import DiagramsRenderer from './diagrams';
import VirtualBook from './VirtualBook';
import helper from './helper';

Expand Down Expand Up @@ -111,8 +111,11 @@ export default {
const rf = pageRenderer.referenceFrame;
rf.innerHTML = pageRenderer.sourceHTML;

// Render UML diagrams first before rendering to page view
return diagramsRenderer(rf)
const pPPFs = pageRenderer.postProcessPromiseFns;

return Promise.all(pPPFs.map(promiseFn =>
promiseFn.process.call(promiseFn, rf)
))
.then(() => {
// Additional element styling
const imgElements = rf.getElementsByTagName('IMG');
Expand Down
17 changes: 10 additions & 7 deletions sashimi-webapp/src/logic/renderer/diagrams.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@
* @return {Promise<string, error>} Promise - containing the HTML string with rendered diagrams
*/


export default function DiagramsRenderer() {
this.mermaid = mermaidAPI;
this.mermaid.initialize({ startOnLoad: false });
}

// Helper function to clear HTML Element
function clearAll(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}

export default function diagramsRenderer(ele) {
DiagramsRenderer.prototype.process = function process(ele) {
const observerConfig = { childList: true };
// get all pre tags with class name = sequence
const seqDiagrams = ele.querySelectorAll('pre.sequence');
Expand Down Expand Up @@ -88,22 +94,19 @@ export default function diagramsRenderer(ele) {
}

// Draws all the mermaid diagrams found
if (mermaidDiagrams.length !== 0) {
mermaidAPI.initialize({ startOnLoad: false });
}
for (let i = 0; i < mermaidDiagrams.length; i+=1) {
let content = mermaidDiagrams[i].innerHTML;
content = content.replace(/&gt;/g, '>');
if (window.mermaidAPI.parse(content)) {
if (this.mermaid.parse(content)) {
const cb = (html, bindFunc) => {
mermaidDiagrams[i].innerHTML = html;
};
mermaidAPI.render(`mermaidChart${i}`, content, cb);
this.mermaid.render(`mermaidChart${i}`, content, cb);
} else {
mermaidDiagrams[i].innerHTML = `<code class='hljs'>${mermaidDiagrams[i].innerHTML}</code>`;
}
}

// returns resolved if all the promises are resolved, otherwise returns rejected
return Promise.all(promiseArr);
}
};
7 changes: 6 additions & 1 deletion sashimi-webapp/src/logic/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import helper from './helper';
* @param {string} page.width - in css width.
* @param {string} page.height - in css height.
* @param {Object} page.padding - for setting the inner the padding size used on the page.
* @param {Array} postProcessingPlugins - an optional array of plugins that contain a `.process` function.
* These functions are to be executed on the referenceFrame.
* These functions will receive the referenceFrame object and
* should return a promise when it is done.
*/
export default function PageRenderer(renderDomTarget, page) {
export default function PageRenderer(renderDomTarget, page, postProcessPromiseFns) {
// Set page sizing. Use default if not provided
this.page = page || defaultConfig.page;
this.renderHeight = helper.computeRenderHeight(this.page);
Expand All @@ -28,6 +32,7 @@ export default function PageRenderer(renderDomTarget, page) {

// Set reference frame
this.referenceFrame = this.getReferenceFrame();
this.postProcessPromiseFns = postProcessPromiseFns || [];

// Set sourceHTML
this.sourceHTML = null;
Expand Down
1 change: 1 addition & 0 deletions sashimi-webapp/static/styles/markdown-imports.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/* Customised mermaid.css taken from hackmd.io, this custom CSS resolves the issue of
the styling applying to the whole HTML document */
@import url("/vendors/mermaid.css");
@import url("/vendors/diagramRendering-fix.css");
6 changes: 6 additions & 0 deletions sashimi-webapp/static/vendors/diagramRendering-fix.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pre.sequence > svg,
pre.flow > svg,
pre.graphviz > svg,
pre.mermaid > svg {
width: 100%
}
20 changes: 12 additions & 8 deletions sashimi-webapp/test/unit/specs/logic/diagramsRenderer.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import domCompare from 'dom-compare';
import diagramsRenderer from 'src/logic/renderer/diagrams';
import DiagramsRenderer from 'src/logic/renderer/diagrams';
import documentPackager from 'src/logic/documentPackager';
import diagramsInput from './reference/diagrams/diagramsInput.txt';
import diagramsRenderedOutput from './reference/diagrams/diagramsRenderedOutput.txt';
Expand Down Expand Up @@ -31,6 +31,9 @@ const iframe = document.createElement('IFRAME');
let iframeDoc;
let toRender;

// DiagramsRender Object
let diagramsRenderer;

/**
* Helper function for checking for 'Expected text' Error (See case 4 in regex helper below)
* @param {String} path - String containing Error Node's path
Expand Down Expand Up @@ -259,12 +262,13 @@ describe('Renderer', () => {
};
// Initialise the above
iframe.onload();
diagramsRenderer = new DiagramsRenderer(iframeDoc);
});

describe('Diagrams Renderer', () => {
it('should handle empty data', (done) => {
toRender = iframeDoc.getElementsByTagName('div')[0];
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((output) => {
expect(toRender.innerHTML).to.equal('');
done();
Expand All @@ -282,7 +286,7 @@ describe('Renderer', () => {
invalidHtmlData.then((output) => {
toRender.innerHTML = output;
// Render any diagrams to be drawn
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(invalidSyntaxOutput, toRender);
Expand All @@ -308,7 +312,7 @@ describe('Renderer', () => {
seqHtmlData.then((output) => {
toRender.innerHTML = output;
// Render any diagrams to be drawn
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(seqDiagramsOutput, toRender);
Expand All @@ -333,7 +337,7 @@ describe('Renderer', () => {
flowHtmlData.then((output) => {
toRender.innerHTML = output;
// Render any diagrams to be drawn
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(flowChartsOutput, toRender);
Expand All @@ -358,7 +362,7 @@ describe('Renderer', () => {
vizHtmlData.then((output) => {
// Render any diagrams to be drawn
toRender.innerHTML = output;
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(graphvizOutput, toRender);
Expand All @@ -383,7 +387,7 @@ describe('Renderer', () => {
mermaidHtmlData.then((output) => {
toRender.innerHTML = output;
// Render any diagrams to be drawn
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(mermaidOutput, toRender);
Expand All @@ -408,7 +412,7 @@ describe('Renderer', () => {
fullHtmlData.then((output) => {
toRender.innerHTML = output;
// Render any diagrams to be drawn
diagramsRenderer(toRender)
diagramsRenderer.process(toRender)
.then((out) => {
// Check if Expected output === Actual rendered output
const diff = compareDom(diagramsRenderedOutput, toRender);
Expand Down

0 comments on commit 8fa02ae

Please sign in to comment.