Adding documentation to regexper.js and some minor tweaks
This commit is contained in:
parent
7de958dc25
commit
d918f956ef
@ -1,3 +1,6 @@
|
|||||||
|
// The Regexper class manages the top-level behavior for the entire
|
||||||
|
// application. This includes event handlers for all user interactions.
|
||||||
|
|
||||||
import util from './util.js';
|
import util from './util.js';
|
||||||
import Parser from './parser/javascript.js';
|
import Parser from './parser/javascript.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -17,7 +20,9 @@ export default class Regexper {
|
|||||||
this.svgContainer = root.querySelector('#regexp-render');
|
this.svgContainer = root.querySelector('#regexp-render');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event handler for key presses in the regular expression form field.
|
||||||
keypressListener(event) {
|
keypressListener(event) {
|
||||||
|
// Pressing Shift-Enter displays the expression.
|
||||||
if (event.shiftKey && event.keyCode === 13) {
|
if (event.shiftKey && event.keyCode === 13) {
|
||||||
event.returnValue = false;
|
event.returnValue = false;
|
||||||
if (event.preventDefault) {
|
if (event.preventDefault) {
|
||||||
@ -28,12 +33,16 @@ export default class Regexper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event handler for key presses while focused anywhere in the application.
|
||||||
documentKeypressListener(event) {
|
documentKeypressListener(event) {
|
||||||
|
// Pressing escape will cancel a currently running render.
|
||||||
if (event.keyCode === 27 && this.running) {
|
if (event.keyCode === 27 && this.running) {
|
||||||
this.running.cancel();
|
this.running.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event handler for submission of the regular expression. Changes the URL
|
||||||
|
// hash which leads to the expression being rendered.
|
||||||
submitListener(event) {
|
submitListener(event) {
|
||||||
event.returnValue = false;
|
event.returnValue = false;
|
||||||
if (event.preventDefault) {
|
if (event.preventDefault) {
|
||||||
@ -44,13 +53,14 @@ export default class Regexper {
|
|||||||
this._setHash(this.field.value);
|
this._setHash(this.field.value);
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
// Most likely failed to set the URL has (probably because the expression
|
// Failed to set the URL hash (probably because the expression is too
|
||||||
// is too long). Turn off the permalink and just show the expression
|
// long). Turn off display of the permalink and just show the expression.
|
||||||
this.permalinkEnabled = false;
|
this.permalinkEnabled = false;
|
||||||
this.showExpression(this.field.value);
|
this.showExpression(this.field.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event handler for URL hash changes. Starts rendering of the expression.
|
||||||
hashchangeListener() {
|
hashchangeListener() {
|
||||||
var expr = this._getHash();
|
var expr = this._getHash();
|
||||||
|
|
||||||
@ -64,6 +74,7 @@ export default class Regexper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Binds all event listeners.
|
||||||
bindListeners() {
|
bindListeners() {
|
||||||
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));
|
||||||
@ -71,10 +82,15 @@ export default class Regexper {
|
|||||||
window.addEventListener('hashchange', this.hashchangeListener.bind(this));
|
window.addEventListener('hashchange', this.hashchangeListener.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the URL hash. This method exists to facilitate automated testing
|
||||||
|
// (since changing the URL can throw off most JavaScript testing tools).
|
||||||
_setHash(hash) {
|
_setHash(hash) {
|
||||||
location.hash = encodeURIComponent(hash);
|
location.hash = encodeURIComponent(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve the current URL hash. This method is also mostly for supporting
|
||||||
|
// automated testing, but also does some basic error handling for malformed
|
||||||
|
// URLs.
|
||||||
_getHash() {
|
_getHash() {
|
||||||
try {
|
try {
|
||||||
return decodeURIComponent(location.hash.slice(1));
|
return decodeURIComponent(location.hash.slice(1));
|
||||||
@ -84,6 +100,11 @@ export default class Regexper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently state of the application. Useful values are:
|
||||||
|
// - `''` - State of the application when the page initially loads
|
||||||
|
// - `'is-loading'` - Displays the loading indicator
|
||||||
|
// - `'has-error'` - Displays the error message
|
||||||
|
// - `'has-results'` - Displays rendered results
|
||||||
set state(state) {
|
set state(state) {
|
||||||
this.root.className = state;
|
this.root.className = state;
|
||||||
}
|
}
|
||||||
@ -92,6 +113,9 @@ export default class Regexper {
|
|||||||
return this.root.className;
|
return this.root.className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the rendering of a regular expression.
|
||||||
|
//
|
||||||
|
// - __expression__ - Regular expression to display.
|
||||||
showExpression(expression) {
|
showExpression(expression) {
|
||||||
this.field.value = expression;
|
this.field.value = expression;
|
||||||
this.state = '';
|
this.state = '';
|
||||||
@ -101,25 +125,32 @@ export default class Regexper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a blob URL for linking to a rendered regular expression image.
|
||||||
|
//
|
||||||
|
// - __content__ - SVG image markup.
|
||||||
buildBlobURL(content) {
|
buildBlobURL(content) {
|
||||||
var blob = new Blob([content], { type: 'image/svg+xml' });
|
// Blob object has to stick around for IE, so the instance is stored on the
|
||||||
window.blob = blob; // Blob object has to stick around for IE
|
// `window` object.
|
||||||
return URL.createObjectURL(blob);
|
window.blob = new Blob([content], { type: 'image/svg+xml' });
|
||||||
|
return URL.createObjectURL(window.blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the URLs of the 'download' and 'permalink' links.
|
||||||
updateLinks() {
|
updateLinks() {
|
||||||
var classes = _.without(this.links.className.split(' '), ['hide-download', 'hide-permalink']);
|
var classes = _.without(this.links.className.split(' '), ['hide-download', 'hide-permalink']);
|
||||||
|
|
||||||
|
// Create the 'download' image URL.
|
||||||
try {
|
try {
|
||||||
this.download.parentNode.style.display = null;
|
this.download.parentNode.style.display = null;
|
||||||
this.download.href = this.buildBlobURL(this.svgContainer.querySelector('.svg').innerHTML);
|
this.download.href = this.buildBlobURL(this.svgContainer.querySelector('.svg').innerHTML);
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch(e) {
|
||||||
// Blobs or URLs created from them don't work here.
|
// Blobs or URLs created from a blob URL don't work in the current
|
||||||
// Giving up on the download link
|
// browser. Giving up on the download link.
|
||||||
classes.push('hide-download');
|
classes.push('hide-download');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the 'permalink' URL.
|
||||||
if (this.permalinkEnabled) {
|
if (this.permalinkEnabled) {
|
||||||
this.permalink.parentNode.style.display = null;
|
this.permalink.parentNode.style.display = null;
|
||||||
this.permalink.href = location.toString();
|
this.permalink.href = location.toString();
|
||||||
@ -130,20 +161,28 @@ export default class Regexper {
|
|||||||
this.links.className = classes.join(' ');
|
this.links.className = classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display any warnings that were generated while rendering a regular expression.
|
||||||
|
//
|
||||||
|
// - __warnings__ - Array of warning messages to display.
|
||||||
displayWarnings(warnings) {
|
displayWarnings(warnings) {
|
||||||
this.warnings.innerHTML = _.map(warnings, warning => {
|
this.warnings.innerHTML = _.map(warnings, warning => {
|
||||||
return `<li class="oi with-text" data-glyph="warning">${warning}</li>`;
|
return `<li class="oi with-text" data-glyph="warning">${warning}</li>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render regular expression
|
||||||
|
//
|
||||||
|
// - __expression__ - Regular expression to render
|
||||||
renderRegexp(expression) {
|
renderRegexp(expression) {
|
||||||
var parseError = false,
|
var parseError = false,
|
||||||
startTime, endTime;
|
startTime, endTime;
|
||||||
|
|
||||||
|
// When a render is already in progress, cancel it and try rendering again
|
||||||
|
// after a short delay (canceling a render is not instantaneous).
|
||||||
if (this.running) {
|
if (this.running) {
|
||||||
this.running.cancel();
|
this.running.cancel();
|
||||||
|
|
||||||
util.wait(10).then(() => {
|
return util.wait(10).then(() => {
|
||||||
return this.renderRegexp(expression);
|
return this.renderRegexp(expression);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,7 +194,9 @@ export default class Regexper {
|
|||||||
this.running = new Parser(this.svgContainer);
|
this.running = new Parser(this.svgContainer);
|
||||||
|
|
||||||
return this.running
|
return this.running
|
||||||
|
// Parse the expression.
|
||||||
.parse(expression)
|
.parse(expression)
|
||||||
|
// Display any error messages from the parser and abort the render.
|
||||||
.catch(message => {
|
.catch(message => {
|
||||||
this.state = 'has-error';
|
this.state = 'has-error';
|
||||||
this.error.innerHTML = '';
|
this.error.innerHTML = '';
|
||||||
@ -165,9 +206,14 @@ export default class Regexper {
|
|||||||
|
|
||||||
throw message;
|
throw message;
|
||||||
})
|
})
|
||||||
|
// When parsing is successful, render the parsed expression.
|
||||||
.then(parser => {
|
.then(parser => {
|
||||||
return parser.render();
|
return parser.render();
|
||||||
})
|
})
|
||||||
|
// Once rendering is complete:
|
||||||
|
// - Update links
|
||||||
|
// - Display any warnings
|
||||||
|
// - Track the completion of the render and how long it took
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.state = 'has-results';
|
this.state = 'has-results';
|
||||||
this.updateLinks();
|
this.updateLinks();
|
||||||
@ -177,6 +223,9 @@ export default class Regexper {
|
|||||||
endTime = new Date().getTime();
|
endTime = new Date().getTime();
|
||||||
window._gaq.push(['_trackTiming', 'visualization', 'total time', endTime - startTime]);
|
window._gaq.push(['_trackTiming', 'visualization', 'total time', endTime - startTime]);
|
||||||
})
|
})
|
||||||
|
// Handle any errors that happened during the rendering pipeline.
|
||||||
|
// Swallows parse errors and render cancellations. Any other exceptions
|
||||||
|
// are allowed to continue on to be tracked by the global error handler.
|
||||||
.catch(message => {
|
.catch(message => {
|
||||||
if (message === 'Render cancelled') {
|
if (message === 'Render cancelled') {
|
||||||
window._gaq.push(['_trackEvent', 'visualization', 'cancelled']);
|
window._gaq.push(['_trackEvent', 'visualization', 'cancelled']);
|
||||||
@ -187,6 +236,8 @@ export default class Regexper {
|
|||||||
throw message;
|
throw message;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// Finally, mark rendering as complete (and pass along any exceptions
|
||||||
|
// that were thrown).
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user