diff --git a/package.json b/package.json
index df95ca1..1cd4943 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"karma-notify-reporter": "^0.1.1",
"karma-phantomjs-launcher": "^0.1.4",
"lodash": "^2.4.1",
+ "q": "^1.1.2",
"snapsvg": "git://github.com/adobe-webplatform/Snap.svg#dev",
"through": "^2.3.6",
"vinyl-transform": "^1.0.0"
diff --git a/src/index.html b/src/index.html
index 846318e..8513cf7 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,10 +1,10 @@
-
diff --git a/src/js/main.js b/src/js/main.js
index 6c0df89..4da46de 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,28 +1,10 @@
-import parser from './parser/javascript.js';
-import Snap from 'snapsvg';
+import Regexper from './regexper.js';
-// Testing code
(function() {
- var result = parser.parse('^test?(foo)[a-z]asdf$'),
- svg = Snap('#regexp-render svg');
+ var regexper = new Regexper(document.body);
+ regexper.bindListeners();
- if (svg) {
- document.body.className = 'has-results';
-
- result.container = svg.group().transform(Snap.matrix()
- .translate(10, 10));
- result.render();
-
- setTimeout(() => {
- var box;
-
- result.position();
-
- box = result.container.getBBox();
- svg.attr({
- width: box.width + 20,
- height: box.height + 20
- });
- });
- }
+ setTimeout(() => {
+ window.dispatchEvent(new Event('hashchange'));
+ });
}());
diff --git a/src/js/regexper.js b/src/js/regexper.js
new file mode 100644
index 0000000..afc4e5a
--- /dev/null
+++ b/src/js/regexper.js
@@ -0,0 +1,117 @@
+import parser from './parser/javascript.js';
+import Snap from 'snapsvg';
+import Q from 'q';
+
+export default class Regexper {
+ constructor(root) {
+ this.root = root;
+ this.form = root.querySelector('#regexp-form');
+ this.field = root.querySelector('#regexp-input');
+ this.error = root.querySelector('#error');
+ this.permalink = root.querySelector('a[data-glyph="link-intact"]');
+ this.download = root.querySelector('a[data-glyph="data-transfer-download"]');
+ this.svg = root.querySelector('#regexp-render svg');
+
+ this.padding = 10;
+ this.snap = Snap(this.svg);
+ }
+
+ keypressListener(event) {
+ if (event.shiftKey && event.keyCode === 13) {
+ event.preventDefault();
+ this.form.dispatchEvent(new Event('submit'));
+ }
+ }
+
+ submitListener(event) {
+ event.preventDefault();
+
+ location.hash = this.field.value;
+ }
+
+ hashchangeListener() {
+ var expression = decodeURIComponent(location.hash.slice(1)).replace(/[\r\n]/g, '');
+
+ if (expression !== '') {
+ this.field.value = expression;
+
+ this.setState('is-loading');
+
+ this.renderRegexp(expression)
+ .then((() => {
+ this.setState('has-results');
+ this.updateLinks();
+ }).bind(this), this.showError.bind(this));
+ }
+ }
+
+ bindListeners() {
+ this.field.addEventListener('keypress', this.keypressListener.bind(this));
+
+ this.form.addEventListener('submit', this.submitListener.bind(this));
+
+ window.addEventListener('hashchange', this.hashchangeListener.bind(this));
+ }
+
+ setState(state) {
+ var classList = this.root.classList;
+
+ classList.remove('is-loading', 'has-results', 'has-error');
+ classList.add(state);
+ }
+
+ showError(message) {
+ this.setState('has-error');
+ this.error.innerHTML = '';
+ this.error.appendChild(document.createTextNode(message));
+ }
+
+ updateLinks() {
+ var blob, url;
+
+ blob = new Blob([svg.outerHTML], { type: 'image/svg+xml' });
+ url = URL.createObjectURL(blob);
+
+ this.download.setAttribute('href', url);
+ this.permalink.setAttribute('href', location);
+ }
+
+ renderSvg(snap, expression) {
+ var result;
+
+ snap.selectAll('g').remove();
+
+ result = parser.parse(expression);
+ result.container = snap.group().transform(Snap.matrix()
+ .translate(this.padding, this.padding));
+ result.render();
+
+ return result;
+ }
+
+ positionSvg(snap, parsed) {
+ var box;
+
+ parsed.position();
+
+ box = parsed.container.getBBox();
+ snap.attr({
+ width: box.width + this.padding * 2,
+ height: box.height + this.padding * 2
+ });
+ }
+
+ renderRegexp(expression) {
+ var padding = this.padding,
+ snap = Snap(this.svg),
+ promise;
+
+ promise = Q.promise(((resolve, reject, notify) => {
+ resolve(this.renderSvg(snap, expression));
+ }).bind(this));
+
+ promise.then(this.positionSvg.bind(this, snap));
+
+ return promise;
+ }
+}
diff --git a/src/sass/main.scss b/src/sass/main.scss
index e68b53d..c802956 100644
--- a/src/sass/main.scss
+++ b/src/sass/main.scss
@@ -175,7 +175,9 @@ header {
#error {
background: $red;
color: $white;
- text-indent: 0.5em;
+ padding: 0 0.5em;
+ white-space: pre;
+ font-family: monospace;
font-weight: bold;
display: none;
@@ -192,7 +194,7 @@ header {
body.is-loading &, body.has-error & {
position: absolute;
- top: -100px;
+ top: -10000px;
}
}