Adding support for canceling a parse/render
This commit is contained in:
parent
bc351bc9fc
commit
c64e48946e
@ -96,6 +96,41 @@ describe('regexper.js', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#documentKeypressListener', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.event = document.createEvent('Event');
|
||||
spyOn(parser, 'cancel');
|
||||
});
|
||||
|
||||
describe('when the keyCode is not 27 (Escape)', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.event.keyCode = 42;
|
||||
});
|
||||
|
||||
it('does not cancel the parser', function() {
|
||||
this.regexper.documentKeypressListener(this.event);
|
||||
expect(parser.cancel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when the keyCode is 27 (Escape)', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
this.event.keyCode = 27;
|
||||
});
|
||||
|
||||
it('cancels the parser', function() {
|
||||
this.regexper.documentKeypressListener(this.event);
|
||||
expect(parser.cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#submitListener', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
@ -168,9 +203,16 @@ describe('regexper.js', function() {
|
||||
|
||||
describe('#bindListeners', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(this.regexper, 'keypressListener');
|
||||
spyOn(this.regexper, 'submitListener');
|
||||
spyOn(this.regexper, 'updatePercentage');
|
||||
spyOn(this.regexper, 'documentKeypressListener');
|
||||
spyOn(this.regexper, 'hashchangeListener');
|
||||
});
|
||||
|
||||
it('binds #keypressListener to keypress on the text field', function() {
|
||||
spyOn(this.regexper.field, 'addEventListener');
|
||||
spyOn(this.regexper, 'keypressListener');
|
||||
this.regexper.bindListeners();
|
||||
expect(this.regexper.field.addEventListener).toHaveBeenCalledWith('keypress', jasmine.any(Function));
|
||||
|
||||
@ -180,7 +222,6 @@ describe('regexper.js', function() {
|
||||
|
||||
it('binds #submitListener to submit on the form', function() {
|
||||
spyOn(this.regexper.form, 'addEventListener');
|
||||
spyOn(this.regexper, 'submitListener');
|
||||
this.regexper.bindListeners();
|
||||
expect(this.regexper.form.addEventListener).toHaveBeenCalledWith('submit', jasmine.any(Function));
|
||||
|
||||
@ -190,17 +231,24 @@ describe('regexper.js', function() {
|
||||
|
||||
it('binds #updatePercentage to updateStatus on the root', function() {
|
||||
spyOn(this.regexper.root, 'addEventListener');
|
||||
spyOn(this.regexper, 'updatePercentage');
|
||||
this.regexper.bindListeners();
|
||||
expect(this.regexper.root.addEventListener).toHaveBeenCalledWith('updateStatus', jasmine.any(Function));
|
||||
|
||||
this.regexper.root.addEventListener.calls.mostRecent().args[1]();
|
||||
this.regexper.root.addEventListener.calls.first().args[1]();
|
||||
expect(this.regexper.updatePercentage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('binds #documentKeypressListener to keyup on the root', function() {
|
||||
spyOn(this.regexper.root, 'addEventListener');
|
||||
this.regexper.bindListeners();
|
||||
expect(this.regexper.root.addEventListener).toHaveBeenCalledWith('keyup', jasmine.any(Function));
|
||||
|
||||
this.regexper.root.addEventListener.calls.mostRecent().args[1]();
|
||||
expect(this.regexper.documentKeypressListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('binds #hashchangeListener to hashchange on the window', function() {
|
||||
spyOn(window, 'addEventListener');
|
||||
spyOn(this.regexper, 'hashchangeListener');
|
||||
this.regexper.bindListeners();
|
||||
expect(window.addEventListener).toHaveBeenCalledWith('hashchange', jasmine.any(Function));
|
||||
|
||||
@ -262,6 +310,21 @@ describe('regexper.js', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('when the parser is cancelled', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
spyOn(this.regexper, 'updateLinks');
|
||||
this.regexper.showExpression('example expression');
|
||||
this.renderPromise.reject('Render cancelled');
|
||||
setTimeout(done, 100);
|
||||
});
|
||||
|
||||
it('clears the state', function() {
|
||||
expect(this.regexper.state).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -41,8 +41,14 @@ parser.Parser.RepeatSpec = { module: RepeatSpec };
|
||||
parser.parse = (parse => {
|
||||
return function() {
|
||||
Subexp.resetCounter();
|
||||
Node.reset();
|
||||
|
||||
return parse.apply(this, arguments);
|
||||
};
|
||||
})(parser.parse);
|
||||
|
||||
parser.cancel = () => {
|
||||
Node.cancelRender = true;
|
||||
};
|
||||
|
||||
export default parser;
|
||||
|
@ -1,9 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import Q from 'q';
|
||||
|
||||
var renderCounter = 0,
|
||||
maxCounter = 0;
|
||||
|
||||
export default class Node {
|
||||
constructor(textValue, offset, elements, properties) {
|
||||
this.textValue = textValue;
|
||||
@ -66,57 +63,69 @@ export default class Node {
|
||||
return this.container.transform(matrix);
|
||||
}
|
||||
|
||||
renderLabel(text) {
|
||||
deferredStep() {
|
||||
var deferred = Q.defer(),
|
||||
group = this.container.group()
|
||||
result = arguments;
|
||||
|
||||
setTimeout(() => {
|
||||
if (Node.cancelRender) {
|
||||
deferred.reject('Render cancelled');
|
||||
} else {
|
||||
deferred.resolve.apply(this, result);
|
||||
}
|
||||
}, 1);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
renderLabel(text) {
|
||||
var group = this.container.group()
|
||||
.addClass('label'),
|
||||
rect = group.rect(),
|
||||
text = group.text(0, 0, _.flatten([text]));
|
||||
|
||||
setTimeout(deferred.resolve.bind(deferred, group));
|
||||
deferred.promise.then(() => {
|
||||
var box = text.getBBox(),
|
||||
margin = 5;
|
||||
return this.deferredStep(group)
|
||||
.then(group => {
|
||||
var box = text.getBBox(),
|
||||
margin = 5;
|
||||
|
||||
text.transform(Snap.matrix()
|
||||
.translate(margin, box.height / 2 + 2 * margin));
|
||||
text.transform(Snap.matrix()
|
||||
.translate(margin, box.height / 2 + 2 * margin));
|
||||
|
||||
rect.attr({
|
||||
width: box.width + 2 * margin,
|
||||
height: box.height + 2 * margin
|
||||
rect.attr({
|
||||
width: box.width + 2 * margin,
|
||||
height: box.height + 2 * margin
|
||||
});
|
||||
|
||||
return group;
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
startRender() {
|
||||
renderCounter++;
|
||||
Node.renderCounter++;
|
||||
}
|
||||
|
||||
doneRender() {
|
||||
var evt, deferred = Q.defer();
|
||||
var evt;
|
||||
|
||||
if (maxCounter === 0) {
|
||||
maxCounter = renderCounter;
|
||||
if (Node.maxCounter === 0) {
|
||||
Node.maxCounter = Node.renderCounter;
|
||||
}
|
||||
|
||||
renderCounter--;
|
||||
Node.renderCounter--;
|
||||
|
||||
evt = document.createEvent('Event');
|
||||
evt.initEvent('updateStatus', true, true);
|
||||
evt.detail = {
|
||||
percentage: (maxCounter - renderCounter) / maxCounter
|
||||
percentage: (Node.maxCounter - Node.renderCounter) / Node.maxCounter
|
||||
};
|
||||
document.body.dispatchEvent(evt);
|
||||
|
||||
if (renderCounter === 0) {
|
||||
maxCounter = 0;
|
||||
if (Node.renderCounter === 0) {
|
||||
Node.maxCounter = 0;
|
||||
}
|
||||
|
||||
setTimeout(deferred.resolve.bind(deferred), 1);
|
||||
|
||||
return deferred.promise;
|
||||
return this.deferredStep();
|
||||
}
|
||||
|
||||
render(container) {
|
||||
@ -191,8 +200,7 @@ export default class Node {
|
||||
}
|
||||
|
||||
renderLabeledBox(label, content, options) {
|
||||
var deferred = Q.defer(),
|
||||
label = this.container.text()
|
||||
var label = this.container.text()
|
||||
.addClass([this.type, 'label'].join('-'))
|
||||
.attr({
|
||||
text: label
|
||||
@ -211,26 +219,30 @@ export default class Node {
|
||||
this.container.prepend(label);
|
||||
this.container.prepend(box);
|
||||
|
||||
setTimeout(deferred.resolve);
|
||||
deferred.promise.then(() => {
|
||||
var labelBox = label.getBBox(),
|
||||
contentBox = content.getBBox();
|
||||
return this.deferredStep()
|
||||
.then(() => {
|
||||
var labelBox = label.getBBox(),
|
||||
contentBox = content.getBBox();
|
||||
|
||||
label.transform(Snap.matrix()
|
||||
.translate(0, labelBox.height));
|
||||
label.transform(Snap.matrix()
|
||||
.translate(0, labelBox.height));
|
||||
|
||||
box
|
||||
.transform(Snap.matrix()
|
||||
.translate(0, labelBox.height))
|
||||
.attr({
|
||||
width: Math.max(contentBox.width + options.padding * 2, labelBox.width),
|
||||
height: contentBox.height + options.padding * 2
|
||||
});
|
||||
box
|
||||
.transform(Snap.matrix()
|
||||
.translate(0, labelBox.height))
|
||||
.attr({
|
||||
width: Math.max(contentBox.width + options.padding * 2, labelBox.width),
|
||||
height: contentBox.height + options.padding * 2
|
||||
});
|
||||
|
||||
content.transform(Snap.matrix()
|
||||
.translate(box.getBBox().cx - contentBox.cx, labelBox.height + options.padding));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
content.transform(Snap.matrix()
|
||||
.translate(box.getBBox().cx - contentBox.cx, labelBox.height + options.padding));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Node.reset = () => {
|
||||
Node.renderCounter = 0;
|
||||
Node.maxCounter = 0;
|
||||
Node.cancelRender = false;
|
||||
};
|
||||
|
@ -32,6 +32,12 @@ export default class Regexper {
|
||||
}
|
||||
}
|
||||
|
||||
documentKeypressListener(event) {
|
||||
if (event.keyCode === 27) {
|
||||
parser.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
submitListener(event) {
|
||||
event.returnValue = false;
|
||||
if (event.preventDefault) {
|
||||
@ -62,6 +68,7 @@ export default class Regexper {
|
||||
this.field.addEventListener('keypress', this.keypressListener.bind(this));
|
||||
this.form.addEventListener('submit', this.submitListener.bind(this));
|
||||
this.root.addEventListener('updateStatus', this.updatePercentage.bind(this));
|
||||
this.root.addEventListener('keyup', this.documentKeypressListener.bind(this));
|
||||
window.addEventListener('hashchange', this.hashchangeListener.bind(this));
|
||||
}
|
||||
|
||||
@ -92,6 +99,12 @@ export default class Regexper {
|
||||
.then(() => {
|
||||
this.state = 'has-results';
|
||||
this.updateLinks();
|
||||
}, (message) => {
|
||||
if (message === 'Render cancelled') {
|
||||
this.state = '';
|
||||
} else {
|
||||
throw message;
|
||||
}
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user