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; } }