Merging the rendering code from main.js and regexper.js
This commit is contained in:
parent
d6e81a2932
commit
e271115d24
@ -6,7 +6,30 @@ import Q from 'q';
|
||||
describe('parser/javascript.js', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.parser = new Parser();
|
||||
this.container = document.createElement('div');
|
||||
this.parser = new Parser(this.container);
|
||||
});
|
||||
|
||||
describe('container property', function() {
|
||||
|
||||
it('sets the content of the element', function() {
|
||||
var element = document.createElement('div');
|
||||
this.parser.container = element;
|
||||
|
||||
expect(element.innerHTML).not.toEqual('');
|
||||
});
|
||||
|
||||
it('keeps the original content if the keepContent option is set', function() {
|
||||
var element = document.createElement('div');
|
||||
element.innerHTML = 'example content';
|
||||
|
||||
this.parser.options.keepContent = true;
|
||||
this.parser.container = element;
|
||||
|
||||
expect(element.innerHTML).toContain('example content');
|
||||
expect(element.innerHTML).not.toEqual('example content');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#parse', function() {
|
||||
@ -15,6 +38,12 @@ describe('parser/javascript.js', function() {
|
||||
spyOn(regexpParser, 'parse');
|
||||
});
|
||||
|
||||
it('adds the "loading" class', function() {
|
||||
spyOn(this.parser, '_addClass');
|
||||
this.parser.parse('example expression');
|
||||
expect(this.parser._addClass).toHaveBeenCalledWith('loading');
|
||||
});
|
||||
|
||||
it('parses the expression', function(done) {
|
||||
this.parser.parse('example expression')
|
||||
.then(() => {
|
||||
@ -59,23 +88,10 @@ describe('parser/javascript.js', function() {
|
||||
this.renderPromise = Q.defer();
|
||||
this.parser.parsed = jasmine.createSpyObj('parsed', ['render']);
|
||||
this.parser.parsed.render.and.returnValue(this.renderPromise.promise);
|
||||
|
||||
this.svgBase = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"></svt>';
|
||||
this.svgContainer = document.createElement('div');
|
||||
});
|
||||
|
||||
it('creates the SVG element', function() {
|
||||
var svg;
|
||||
|
||||
this.parser.render(this.svgContainer, this.svgBase);
|
||||
|
||||
svg = this.svgContainer.querySelector('svg');
|
||||
expect(svg.getAttribute('xmlns')).toEqual('http://www.w3.org/2000/svg');
|
||||
expect(svg.getAttribute('version')).toEqual('1.1');
|
||||
});
|
||||
|
||||
it('render the parsed expression', function() {
|
||||
this.parser.render(this.svgContainer, this.svgBase);
|
||||
this.parser.render();
|
||||
expect(this.parser.parsed.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -90,12 +106,11 @@ describe('parser/javascript.js', function() {
|
||||
height: 24
|
||||
});
|
||||
|
||||
this.parser.render(this.svgContainer, this.svgBase);
|
||||
this.renderPromise.resolve(this.result);
|
||||
});
|
||||
|
||||
it('positions the renderd expression', function(done) {
|
||||
this.parser.render(this.svgContainer, this.svgBase)
|
||||
this.parser.render()
|
||||
.then(() => {
|
||||
expect(this.result.transform).toHaveBeenCalledWith(Snap.matrix()
|
||||
.translate(6, 8));
|
||||
@ -105,9 +120,9 @@ describe('parser/javascript.js', function() {
|
||||
});
|
||||
|
||||
it('sets the dimensions of the image', function(done) {
|
||||
this.parser.render(this.svgContainer, this.svgBase)
|
||||
this.parser.render()
|
||||
.then(() => {
|
||||
var svg = this.svgContainer.querySelector('svg');
|
||||
var svg = this.container.querySelector('svg');
|
||||
|
||||
expect(svg.getAttribute('width')).toEqual('62');
|
||||
expect(svg.getAttribute('height')).toEqual('44');
|
||||
@ -116,6 +131,25 @@ describe('parser/javascript.js', function() {
|
||||
.done();
|
||||
});
|
||||
|
||||
it('removes the "loading" class', function(done) {
|
||||
spyOn(this.parser, '_removeClass');
|
||||
this.parser.render()
|
||||
.then(() => {
|
||||
expect(this.parser._removeClass).toHaveBeenCalledWith('loading');
|
||||
})
|
||||
.finally(done)
|
||||
.done();
|
||||
});
|
||||
|
||||
it('removes the progress element', function(done) {
|
||||
this.parser.render()
|
||||
.then(() => {
|
||||
expect(this.container.querySelector('.loading')).toBeNull();
|
||||
})
|
||||
.finally(done)
|
||||
.done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -15,7 +15,6 @@ describe('regexper.js', function() {
|
||||
'<div><a href="#" data-glyph="link-intact"></a></div>',
|
||||
'<div><a href="#" data-glyph="data-transfer-download"></a></div>',
|
||||
'<div id="regexp-render"></div>',
|
||||
'<script type="text/html" id="svg-base"><svg></svg></script>'
|
||||
].join('');
|
||||
|
||||
this.regexper = new Regexper(this.root);
|
||||
@ -102,7 +101,7 @@ describe('regexper.js', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.event = util.customEvent('keyup');
|
||||
this.regexper.runningParser = jasmine.createSpyObj('parser', ['cancel']);
|
||||
this.regexper.running = jasmine.createSpyObj('parser', ['cancel']);
|
||||
});
|
||||
|
||||
describe('when the keyCode is not 27 (Escape)', function() {
|
||||
@ -113,7 +112,7 @@ describe('regexper.js', function() {
|
||||
|
||||
it('does not cancel the parser', function() {
|
||||
this.regexper.documentKeypressListener(this.event);
|
||||
expect(this.regexper.runningParser.cancel).not.toHaveBeenCalled();
|
||||
expect(this.regexper.running.cancel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
@ -126,7 +125,7 @@ describe('regexper.js', function() {
|
||||
|
||||
it('cancels the parser', function() {
|
||||
this.regexper.documentKeypressListener(this.event);
|
||||
expect(this.regexper.runningParser.cancel).toHaveBeenCalled();
|
||||
expect(this.regexper.running.cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
@ -375,14 +374,14 @@ describe('regexper.js', function() {
|
||||
expect(this.regexper._trackEvent).toHaveBeenCalledWith('visualization', 'start');
|
||||
});
|
||||
|
||||
it('keeps a copy of the running parser', function() {
|
||||
it('keeps a copy of the running property parser', function() {
|
||||
this.regexper.renderRegexp('example expression');
|
||||
expect(this.regexper.runningParser).toBeTruthy();
|
||||
expect(this.regexper.running).toBeTruthy();
|
||||
});
|
||||
|
||||
it('parses the expression', function() {
|
||||
this.regexper.renderRegexp('example expression');
|
||||
expect(this.regexper.runningParser.parse).toHaveBeenCalledWith('example expression');
|
||||
expect(this.regexper.running.parse).toHaveBeenCalledWith('example expression');
|
||||
});
|
||||
|
||||
describe('when parsing fails', function() {
|
||||
@ -423,7 +422,7 @@ describe('regexper.js', function() {
|
||||
describe('when parsing succeeds', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.parser = new Parser();
|
||||
this.parser = new Parser(this.regexper.svgContainer);
|
||||
this.parsePromise.resolve(this.parser);
|
||||
this.renderPromise.resolve();
|
||||
});
|
||||
@ -431,7 +430,7 @@ describe('regexper.js', function() {
|
||||
it('renders the expression', function(done) {
|
||||
this.regexper.renderRegexp('example expression')
|
||||
.then(() => {
|
||||
expect(this.parser.render).toHaveBeenCalledWith(this.regexper.svgContainer.querySelector('.svg'), this.regexper.svgBase);
|
||||
expect(this.parser.render).toHaveBeenCalled();
|
||||
}, fail)
|
||||
.finally(done)
|
||||
.done();
|
||||
@ -442,7 +441,7 @@ describe('regexper.js', function() {
|
||||
describe('when rendering is complete', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.parser = new Parser();
|
||||
this.parser = new Parser(this.regexper.svgContainer);
|
||||
this.parsePromise.resolve(this.parser);
|
||||
this.renderPromise.resolve();
|
||||
});
|
||||
@ -483,10 +482,10 @@ describe('regexper.js', function() {
|
||||
.done();
|
||||
});
|
||||
|
||||
it('sets the runningParser to false', function(done) {
|
||||
it('sets the running property to false', function(done) {
|
||||
this.regexper.renderRegexp('example expression')
|
||||
.then(() => {
|
||||
expect(this.regexper.runningParser).toBeFalsy();
|
||||
expect(this.regexper.running).toBeFalsy();
|
||||
}, fail)
|
||||
.finally(done)
|
||||
.done();
|
||||
@ -497,7 +496,7 @@ describe('regexper.js', function() {
|
||||
describe('when the rendering is cancelled', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.parser = new Parser();
|
||||
this.parser = new Parser(this.regexper.svgContainer);
|
||||
this.parsePromise.resolve(this.parser);
|
||||
this.renderPromise.reject('Render cancelled');
|
||||
});
|
||||
@ -520,10 +519,10 @@ describe('regexper.js', function() {
|
||||
.done();
|
||||
});
|
||||
|
||||
it('sets the runningParser to false', function(done) {
|
||||
it('sets the running property to false', function(done) {
|
||||
this.regexper.renderRegexp('example expression')
|
||||
.then(() => {
|
||||
expect(this.regexper.runningParser).toBeFalsy();
|
||||
expect(this.regexper.running).toBeFalsy();
|
||||
}, fail)
|
||||
.finally(done)
|
||||
.done();
|
||||
@ -534,15 +533,15 @@ describe('regexper.js', function() {
|
||||
describe('when the rendering fails', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.parser = new Parser();
|
||||
this.parser = new Parser(this.regexper.svgContainer);
|
||||
this.parsePromise.resolve(this.parser);
|
||||
this.renderPromise.reject('example render failure');
|
||||
});
|
||||
|
||||
it('sets the runningParser to false', function(done) {
|
||||
it('sets the running property to false', function(done) {
|
||||
this.regexper.renderRegexp('example expression')
|
||||
.then(fail, () => {
|
||||
expect(this.regexper.runningParser).toBeFalsy();
|
||||
expect(this.regexper.running).toBeFalsy();
|
||||
})
|
||||
.finally(done)
|
||||
.done();
|
||||
|
14
spec/setup_spec.js
Normal file
14
spec/setup_spec.js
Normal file
@ -0,0 +1,14 @@
|
||||
beforeEach(function() {
|
||||
var template = document.createElement('script');
|
||||
template.setAttribute('type', 'text/html');
|
||||
template.setAttribute('id', 'svg-container-base');
|
||||
template.innerHTML = [
|
||||
'<div class="svg"><svg></svg></div>',
|
||||
'<div class="progress"><div></div></div>'
|
||||
].join('');
|
||||
document.body.appendChild(template);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.body.removeChild(document.body.querySelector('#svg-container-base'));
|
||||
});
|
@ -27,31 +27,10 @@ window._gaq = (typeof _gaq !== 'undefined') ? _gaq : {
|
||||
});
|
||||
}
|
||||
|
||||
_.each(document.querySelectorAll('figure[data-expr]'), element => {
|
||||
var parser = new Parser(),
|
||||
svg, percentage;
|
||||
|
||||
element.className = _.compact([element.className, 'loading']).join(' ');
|
||||
element.innerHTML = [
|
||||
'<div class="svg"></div>',
|
||||
'<div class="progress"><div style="width: 0;"></div></div>',
|
||||
element.innerHTML
|
||||
].join('');
|
||||
|
||||
svg = element.querySelector('.svg');
|
||||
percentage = element.querySelector('.progress div');
|
||||
|
||||
setTimeout(() => {
|
||||
parser.parse(element.getAttribute('data-expr'))
|
||||
.invoke('render', svg, document.querySelector('#svg-base').innerHTML)
|
||||
.then(null, null, progress => {
|
||||
percentage.style.width = progress * 100 + '%';
|
||||
})
|
||||
.finally(() => {
|
||||
element.className = _.without(element.className.split(' '), 'loading').join(' ');
|
||||
element.removeChild(element.querySelector('.progress'));
|
||||
})
|
||||
.done();
|
||||
}, 1);
|
||||
_.each(document.querySelectorAll('[data-expr]'), element => {
|
||||
new Parser(element, { keepContent: true })
|
||||
.parse(element.getAttribute('data-expr'))
|
||||
.invoke('render')
|
||||
.done();
|
||||
});
|
||||
}());
|
||||
|
@ -1,10 +1,11 @@
|
||||
import Q from 'q';
|
||||
import Snap from 'snapsvg';
|
||||
import _ from 'lodash';
|
||||
|
||||
import javascript from './javascript/parser.js';
|
||||
|
||||
export default class Parser {
|
||||
constructor() {
|
||||
constructor(container, options) {
|
||||
this.state = {
|
||||
groupCounter: 1,
|
||||
renderCounter: 0,
|
||||
@ -12,11 +13,40 @@ export default class Parser {
|
||||
cancelRender: false,
|
||||
warnings: []
|
||||
};
|
||||
|
||||
this.options = options || {};
|
||||
_.defaults(this.options, {
|
||||
keepContent: false
|
||||
});
|
||||
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
set container(cont) {
|
||||
this._container = cont;
|
||||
this._container.innerHTML = [
|
||||
document.querySelector('#svg-container-base').innerHTML,
|
||||
this.options.keepContent ? this.container.innerHTML : ''
|
||||
].join('');
|
||||
}
|
||||
|
||||
get container() {
|
||||
return this._container;
|
||||
}
|
||||
|
||||
_addClass(className) {
|
||||
this.container.className = _.compact([this.container.className, className]).join(' ');
|
||||
}
|
||||
|
||||
_removeClass(className) {
|
||||
this.container.className = _.without(this.container.className.split(' '), className).join(' ');
|
||||
}
|
||||
|
||||
parse(expression) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
this._addClass('loading');
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
javascript.Parser.SyntaxNode.state = this.state;
|
||||
@ -32,23 +62,30 @@ export default class Parser {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
render(containerElement, svgBase) {
|
||||
var svg;
|
||||
|
||||
containerElement.innerHTML = svgBase;
|
||||
|
||||
svg = Snap(containerElement.querySelector('svg'));
|
||||
render() {
|
||||
var svg = Snap(this.container.querySelector('svg')),
|
||||
progress = this.container.querySelector('.progress div');
|
||||
|
||||
return this.parsed.render(svg.group())
|
||||
.then(result => {
|
||||
var box = result.getBBox();
|
||||
.then(
|
||||
result => {
|
||||
var box = result.getBBox();
|
||||
|
||||
result.transform(Snap.matrix()
|
||||
.translate(10 - box.x, 10 - box.y));
|
||||
svg.attr({
|
||||
width: box.width + 20,
|
||||
height: box.height + 20
|
||||
});
|
||||
result.transform(Snap.matrix()
|
||||
.translate(10 - box.x, 10 - box.y));
|
||||
svg.attr({
|
||||
width: box.width + 20,
|
||||
height: box.height + 20
|
||||
});
|
||||
},
|
||||
null,
|
||||
percent => {
|
||||
progress.style.width = percent * 100 + '%';
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
this._removeClass('loading');
|
||||
this.container.removeChild(this.container.querySelector('.progress'));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ export default class Regexper {
|
||||
this.permalink = root.querySelector('a[data-glyph="link-intact"]');
|
||||
this.download = root.querySelector('a[data-glyph="data-transfer-download"]');
|
||||
this.svgContainer = root.querySelector('#regexp-render');
|
||||
this.svgBase = this.root.querySelector('#svg-base').innerHTML;
|
||||
}
|
||||
|
||||
keypressListener(event) {
|
||||
@ -28,8 +27,8 @@ export default class Regexper {
|
||||
}
|
||||
|
||||
documentKeypressListener(event) {
|
||||
if (event.keyCode === 27 && this.runningParser) {
|
||||
this.runningParser.cancel();
|
||||
if (event.keyCode === 27 && this.running) {
|
||||
this.running.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,12 +122,12 @@ export default class Regexper {
|
||||
}
|
||||
|
||||
renderRegexp(expression) {
|
||||
var svg, percentage;
|
||||
var parseError = false;
|
||||
|
||||
if (this.runningParser) {
|
||||
if (this.running) {
|
||||
let deferred = Q.defer();
|
||||
|
||||
this.runningParser.cancel();
|
||||
this.running.cancel();
|
||||
|
||||
setTimeout(() => {
|
||||
deferred.resolve(this.renderRegexp(expression));
|
||||
@ -140,53 +139,38 @@ export default class Regexper {
|
||||
this.state = 'is-loading';
|
||||
this._trackEvent('visualization', 'start');
|
||||
|
||||
this.runningParser = new Parser();
|
||||
this.running = new Parser(this.svgContainer);
|
||||
|
||||
this.svgContainer.innerHTML = [
|
||||
'<div class="svg"></div>',
|
||||
'<div class="progress"><div style="width: 0;"></div></div>',
|
||||
].join('');
|
||||
|
||||
svg = this.svgContainer.querySelector('.svg');
|
||||
percentage = this.svgContainer.querySelector('.progress div');
|
||||
|
||||
return this.runningParser.parse(expression)
|
||||
return this.running
|
||||
.parse(expression)
|
||||
.then(null, message => {
|
||||
this.state = 'has-error';
|
||||
this.error.innerHTML = '';
|
||||
this.error.appendChild(document.createTextNode(message));
|
||||
|
||||
this.parseError = true;
|
||||
parseError = true;
|
||||
|
||||
throw message;
|
||||
})
|
||||
.invoke('render', svg, this.svgBase)
|
||||
.then(
|
||||
() => {
|
||||
.invoke('render')
|
||||
.then(() => {
|
||||
this.state = 'has-results';
|
||||
this.updateLinks();
|
||||
this.displayWarnings(this.runningParser.warnings);
|
||||
this.displayWarnings(this.running.warnings);
|
||||
this._trackEvent('visualization', 'complete');
|
||||
},
|
||||
null,
|
||||
progress => {
|
||||
percentage.style.width = progress * 100 + '%';
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(null, message => {
|
||||
if (message === 'Render cancelled') {
|
||||
this._trackEvent('visualization', 'cancelled');
|
||||
this.state = '';
|
||||
} else if (this.parseError) {
|
||||
} else if (parseError) {
|
||||
this._trackEvent('visualization', 'parse error');
|
||||
} else {
|
||||
throw message;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.runningParser = false;
|
||||
this.parseError = false;
|
||||
this.svgContainer.removeChild(this.svgContainer.querySelector('.progress'));
|
||||
this.running = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -24,27 +24,32 @@
|
||||
</div>
|
||||
<![endif]-->
|
||||
<!--[if gt IE 8]> -->
|
||||
<script type="text/html" id="svg-base">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
version="1.1">
|
||||
<defs>
|
||||
<style type="text/css">${svgStyles}</style>
|
||||
</defs>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/">
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
<script type="text/html" id="svg-container-base">
|
||||
<div class="svg">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
version="1.1">
|
||||
<defs>
|
||||
<style type="text/css">${svgStyles}</style>
|
||||
</defs>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/">
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div style="width:0;"></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<header>
|
||||
|
Loading…
Reference in New Issue
Block a user