From 47a3dc8ba54b59deb825fd0762400db078b1a8af Mon Sep 17 00:00:00 2001 From: Jeremy Likness Date: Tue, 26 Nov 2019 09:15:12 -0800 Subject: [PATCH] Refactor data-binding to own class --- js/dataBinding.js | 50 +++++++++++++++++++++++++++++++++++++++++++++++ js/slide.js | 42 ++++++++++----------------------------- 2 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 js/dataBinding.js diff --git a/js/dataBinding.js b/js/dataBinding.js new file mode 100644 index 0000000..7f64211 --- /dev/null +++ b/js/dataBinding.js @@ -0,0 +1,50 @@ +// @ts-check + +export class DataBinding { + + /** + * Simple evaluation + * @param {string} js The JavaScript to evaluate + */ + execute(js) { + return eval(js); + } + + /** + * Evaluates JavaScript with a constrained context (scope) + * @param {string} src The JavaScript to evaluate + * @param {object} context The context (data) to evaluate with + * @returns {object} The result of the evaluation + */ + executeInContext(src, context) { + return this.execute.call(context, src); + } + + /** + * Searches for "repeat" attribute to data-bind lists + * @param {HTMLElement} elem The parent element to search + * @param {object} context The context to use for binding + */ + bindLists(elem, context) { + const listBinding = elem.querySelectorAll("[repeat]"); + listBinding.forEach(elem => { + const parent = elem.parentElement; + const expression = elem.getAttribute("repeat"); + elem.removeAttribute("repeat"); + const template = elem.outerHTML; + parent.removeChild(elem); + context[expression].forEach(item => { + let newTemplate = `${template}`; + const matches = newTemplate.match(/\{\{([^\}]*?)\}\}/g); + if (matches) { + matches.forEach(match => { + match = match.replace("{{", "").replace("}}", ""); + const value = this.executeInContext(`this.${match}`, {item}); + newTemplate = newTemplate.replace(`{{${match}}}`, value); + }); + parent.innerHTML += newTemplate; + } + }); + }); + } +} \ No newline at end of file diff --git a/js/slide.js b/js/slide.js index 7f9f770..e04a37b 100644 --- a/js/slide.js +++ b/js/slide.js @@ -1,4 +1,7 @@ // @ts-check + +import {DataBinding} from "./dataBinding.js" + /** * Represents a slide * */ @@ -19,6 +22,11 @@ export class Slide { * @type {object} */ this._context = {}; + /** + * Data binding helper + * @type {DataBinding} + */ + this._dataBinding = new DataBinding(); /** * The HTML DOM hosting the slide contents * @type {HTMLDivElement} @@ -57,41 +65,11 @@ export class Slide { // execute any scripts const script = this._html.querySelector("script"); if (script) { - (function (/** @type {string} */src) { - return eval(src) - }).call(this._context, script.innerText); - this.dataBind(); + this._dataBinding.executeInContext(script.innerText, this._context); + this._dataBinding.bindLists(this._html, this._context); } } - /** - * Scans for data-binding and applies the bindings - */ - dataBind() { - const listBinding = this._html.querySelectorAll("[repeat]"); - listBinding.forEach(elem => { - const parent = elem.parentElement; - const expression = elem.getAttribute("repeat"); - elem.removeAttribute("repeat"); - const template = elem.outerHTML; - parent.removeChild(elem); - this._context[expression].forEach(item => { - let newTemplate = `${template}`; - const matches = newTemplate.match(/\{\{([^\}]*?)\}\}/g); - if (matches) { - matches.forEach(match => { - match = match.replace("{{", "").replace("}}", ""); - const value = (function (/** @type {string} */src) { - return eval(src) - }).call({ item }, match); - newTemplate = newTemplate.replace(`{{${match}}}`, value); - }); - parent.innerHTML += newTemplate; - } - }); - }); - } - /** * The slide transition * @returns {string} The transition name