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() {
|
describe('#submitListener', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@ -168,9 +203,16 @@ describe('regexper.js', function() {
|
|||||||
|
|
||||||
describe('#bindListeners', 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() {
|
it('binds #keypressListener to keypress on the text field', function() {
|
||||||
spyOn(this.regexper.field, 'addEventListener');
|
spyOn(this.regexper.field, 'addEventListener');
|
||||||
spyOn(this.regexper, 'keypressListener');
|
|
||||||
this.regexper.bindListeners();
|
this.regexper.bindListeners();
|
||||||
expect(this.regexper.field.addEventListener).toHaveBeenCalledWith('keypress', jasmine.any(Function));
|
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() {
|
it('binds #submitListener to submit on the form', function() {
|
||||||
spyOn(this.regexper.form, 'addEventListener');
|
spyOn(this.regexper.form, 'addEventListener');
|
||||||
spyOn(this.regexper, 'submitListener');
|
|
||||||
this.regexper.bindListeners();
|
this.regexper.bindListeners();
|
||||||
expect(this.regexper.form.addEventListener).toHaveBeenCalledWith('submit', jasmine.any(Function));
|
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() {
|
it('binds #updatePercentage to updateStatus on the root', function() {
|
||||||
spyOn(this.regexper.root, 'addEventListener');
|
spyOn(this.regexper.root, 'addEventListener');
|
||||||
spyOn(this.regexper, 'updatePercentage');
|
|
||||||
this.regexper.bindListeners();
|
this.regexper.bindListeners();
|
||||||
expect(this.regexper.root.addEventListener).toHaveBeenCalledWith('updateStatus', jasmine.any(Function));
|
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();
|
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() {
|
it('binds #hashchangeListener to hashchange on the window', function() {
|
||||||
spyOn(window, 'addEventListener');
|
spyOn(window, 'addEventListener');
|
||||||
spyOn(this.regexper, 'hashchangeListener');
|
|
||||||
this.regexper.bindListeners();
|
this.regexper.bindListeners();
|
||||||
expect(window.addEventListener).toHaveBeenCalledWith('hashchange', jasmine.any(Function));
|
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 => {
|
parser.parse = (parse => {
|
||||||
return function() {
|
return function() {
|
||||||
Subexp.resetCounter();
|
Subexp.resetCounter();
|
||||||
|
Node.reset();
|
||||||
|
|
||||||
return parse.apply(this, arguments);
|
return parse.apply(this, arguments);
|
||||||
};
|
};
|
||||||
})(parser.parse);
|
})(parser.parse);
|
||||||
|
|
||||||
|
parser.cancel = () => {
|
||||||
|
Node.cancelRender = true;
|
||||||
|
};
|
||||||
|
|
||||||
export default parser;
|
export default parser;
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
|
|
||||||
var renderCounter = 0,
|
|
||||||
maxCounter = 0;
|
|
||||||
|
|
||||||
export default class Node {
|
export default class Node {
|
||||||
constructor(textValue, offset, elements, properties) {
|
constructor(textValue, offset, elements, properties) {
|
||||||
this.textValue = textValue;
|
this.textValue = textValue;
|
||||||
@ -66,57 +63,69 @@ export default class Node {
|
|||||||
return this.container.transform(matrix);
|
return this.container.transform(matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLabel(text) {
|
deferredStep() {
|
||||||
var deferred = Q.defer(),
|
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'),
|
.addClass('label'),
|
||||||
rect = group.rect(),
|
rect = group.rect(),
|
||||||
text = group.text(0, 0, _.flatten([text]));
|
text = group.text(0, 0, _.flatten([text]));
|
||||||
|
|
||||||
setTimeout(deferred.resolve.bind(deferred, group));
|
return this.deferredStep(group)
|
||||||
deferred.promise.then(() => {
|
.then(group => {
|
||||||
var box = text.getBBox(),
|
var box = text.getBBox(),
|
||||||
margin = 5;
|
margin = 5;
|
||||||
|
|
||||||
text.transform(Snap.matrix()
|
text.transform(Snap.matrix()
|
||||||
.translate(margin, box.height / 2 + 2 * margin));
|
.translate(margin, box.height / 2 + 2 * margin));
|
||||||
|
|
||||||
rect.attr({
|
rect.attr({
|
||||||
width: box.width + 2 * margin,
|
width: box.width + 2 * margin,
|
||||||
height: box.height + 2 * margin
|
height: box.height + 2 * margin
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startRender() {
|
startRender() {
|
||||||
renderCounter++;
|
Node.renderCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
doneRender() {
|
doneRender() {
|
||||||
var evt, deferred = Q.defer();
|
var evt;
|
||||||
|
|
||||||
if (maxCounter === 0) {
|
if (Node.maxCounter === 0) {
|
||||||
maxCounter = renderCounter;
|
Node.maxCounter = Node.renderCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCounter--;
|
Node.renderCounter--;
|
||||||
|
|
||||||
evt = document.createEvent('Event');
|
evt = document.createEvent('Event');
|
||||||
evt.initEvent('updateStatus', true, true);
|
evt.initEvent('updateStatus', true, true);
|
||||||
evt.detail = {
|
evt.detail = {
|
||||||
percentage: (maxCounter - renderCounter) / maxCounter
|
percentage: (Node.maxCounter - Node.renderCounter) / Node.maxCounter
|
||||||
};
|
};
|
||||||
document.body.dispatchEvent(evt);
|
document.body.dispatchEvent(evt);
|
||||||
|
|
||||||
if (renderCounter === 0) {
|
if (Node.renderCounter === 0) {
|
||||||
maxCounter = 0;
|
Node.maxCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(deferred.resolve.bind(deferred), 1);
|
return this.deferredStep();
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(container) {
|
render(container) {
|
||||||
@ -191,8 +200,7 @@ export default class Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLabeledBox(label, content, options) {
|
renderLabeledBox(label, content, options) {
|
||||||
var deferred = Q.defer(),
|
var label = this.container.text()
|
||||||
label = this.container.text()
|
|
||||||
.addClass([this.type, 'label'].join('-'))
|
.addClass([this.type, 'label'].join('-'))
|
||||||
.attr({
|
.attr({
|
||||||
text: label
|
text: label
|
||||||
@ -211,26 +219,30 @@ export default class Node {
|
|||||||
this.container.prepend(label);
|
this.container.prepend(label);
|
||||||
this.container.prepend(box);
|
this.container.prepend(box);
|
||||||
|
|
||||||
setTimeout(deferred.resolve);
|
return this.deferredStep()
|
||||||
deferred.promise.then(() => {
|
.then(() => {
|
||||||
var labelBox = label.getBBox(),
|
var labelBox = label.getBBox(),
|
||||||
contentBox = content.getBBox();
|
contentBox = content.getBBox();
|
||||||
|
|
||||||
label.transform(Snap.matrix()
|
label.transform(Snap.matrix()
|
||||||
.translate(0, labelBox.height));
|
.translate(0, labelBox.height));
|
||||||
|
|
||||||
box
|
box
|
||||||
.transform(Snap.matrix()
|
.transform(Snap.matrix()
|
||||||
.translate(0, labelBox.height))
|
.translate(0, labelBox.height))
|
||||||
.attr({
|
.attr({
|
||||||
width: Math.max(contentBox.width + options.padding * 2, labelBox.width),
|
width: Math.max(contentBox.width + options.padding * 2, labelBox.width),
|
||||||
height: contentBox.height + options.padding * 2
|
height: contentBox.height + options.padding * 2
|
||||||
});
|
});
|
||||||
|
|
||||||
content.transform(Snap.matrix()
|
content.transform(Snap.matrix()
|
||||||
.translate(box.getBBox().cx - contentBox.cx, labelBox.height + options.padding));
|
.translate(box.getBBox().cx - contentBox.cx, labelBox.height + options.padding));
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
submitListener(event) {
|
||||||
event.returnValue = false;
|
event.returnValue = false;
|
||||||
if (event.preventDefault) {
|
if (event.preventDefault) {
|
||||||
@ -62,6 +68,7 @@ export default class Regexper {
|
|||||||
this.field.addEventListener('keypress', this.keypressListener.bind(this));
|
this.field.addEventListener('keypress', this.keypressListener.bind(this));
|
||||||
this.form.addEventListener('submit', this.submitListener.bind(this));
|
this.form.addEventListener('submit', this.submitListener.bind(this));
|
||||||
this.root.addEventListener('updateStatus', this.updatePercentage.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));
|
window.addEventListener('hashchange', this.hashchangeListener.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +99,12 @@ export default class Regexper {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
this.state = 'has-results';
|
this.state = 'has-results';
|
||||||
this.updateLinks();
|
this.updateLinks();
|
||||||
|
}, (message) => {
|
||||||
|
if (message === 'Render cancelled') {
|
||||||
|
this.state = '';
|
||||||
|
} else {
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.done();
|
.done();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user