UNPKG

@joshuafcole/fluorine

Version:

A highly reactive, optionally compiled, and strongly typed view layer for TypeScript.

289 lines (288 loc) 9.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("efreet/utils"); function embed(val) { return val === undefined ? "undefined" : val instanceof Compilable ? val.compile() : JSON.stringify(val); } exports.embed = embed; //---------------------------------------------------------------------- // Event Binder (statically generated event handlers) //---------------------------------------------------------------------- class EventBinder { // @NOTE: Working around a gnarly issue in the type system. constructor(_handle, _handler, _prior_clause) { this._handle = _handle; this._handler = _handler; this._prior_clause = _prior_clause; this._thens = []; } compile_inner() { // pad_chunk(2) let inner = ` ${this._handler} ${this._preventer} ${this._stopper} ${this._thens.join("\n")} `; if (this._when) { // pad_chunk(2) inner = ` if(${this._when}) { ${inner} } `; } if (this._prior_clause) { // The inverted order here is important because we're unwinding the linked-list :/ inner += "\n\n // OR\n binder = binder._prior_clause;\n" + this._prior_clause.compile_inner(); } return inner; } compile() { let start = utils_1.now(); let inner = this.compile_inner(); let compiled = new Function("binder", // pad_chunk(2) ` return function catalyst_compiled_handler(event, elem) { var target = event.currentTarget || event.target; ${inner} }; `)(this); // console.log("COMPILED", this._handle && this._handle.name, now() - start); // console.log(compiled); return compiled; } /** Trigger the handler with the given properties when this event fires. */ static handle(handler, ...args) { return new EventBinder(handler, // inline_chunk ` // ${handler.name} binder._handle(${args.map(embed).join(", ")}); `); } /** Add another non-exclusive handling clause. Useful in conjunction with `when()`. */ or(handler, ...args) { return new EventBinder(handler, // inline_chunk ` // ${handler.name} binder._handle(${args.map(embed).join(", ")}); `, this); } /** Add a condition which must be true for the handler to fire. */ when(prop) { this._when = embed(prop); return this; } /** Add a side effect to be triggered after the handler fires. */ then(effect) { this._thens.push(effect.compile()); return this; } /** Always stop propagation on this event. */ stop() { this._stopper = `event.stopPropagation();`; return this; } /** Always prevent default on this event. */ prevent() { this._preventer = `event.preventDefault();`; return this; } /** Stop propagation on this event if the predicate returns true. */ stop_if(predicate, ...args) { this._stop = predicate; // inline_chunk this._stopper = ` // ${predicate.name} if(binder._stop(${args.map(embed).join(", ")})) { event.stopPropagation(); } `; return this; } /** Prevent default on this event if the predicate returns true. */ prevent_if(predicate, ...args) { this._prevent = predicate; // inline_chunk this._preventer = ` // ${predicate.name} if(binder._prevent(${args.map(embed).join(", ")})) { event.preventDefault(); } `; return this; } } exports.EventBinder = EventBinder; //---------------------------------------------------------------------- // Compilables (statically defined snippets for generating event handlers) //---------------------------------------------------------------------- class Compilable { equals(rhs) { return new Compilable.Infix("===", this, rhs); } is_not(rhs) { return new Compilable.Infix("!==", this, rhs); } dot(property) { return new Compilable.Dot(this, property); } and(rhs) { return new Compilable.Infix("&&", this, rhs); } or(rhs) { return new Compilable.Infix("||", this, rhs); } to_string() { return new Compilable.Computed(`""+$1`, this); } to_number() { return new Compilable.Computed(`!isNaN(+$1) ? +$1 : undefined`, this); } } exports.Compilable = Compilable; (function (Compilable) { class Snippet extends Compilable { constructor(code) { super(); this.code = code; } compile() { return this.code; } } Compilable.Snippet = Snippet; class Computed extends Compilable { constructor(template, ...terms) { super(); this.template = template; this.terms = terms; } compile() { let code = this.template; for (let ix = 0; ix < this.terms.length; ix += 1) { code = code.replace(new RegExp(`\\$${ix + 1}`, "g"), embed(this.terms[ix])); } return code; } } Compilable.Computed = Computed; class Infix extends Compilable { constructor(op, lhs, rhs) { super(); this.op = op; this.lhs = lhs; this.rhs = rhs; } compile() { return `(${embed(this.lhs)} ${this.op} ${embed(this.rhs)})`; } } Compilable.Infix = Infix; class Dot extends Compilable { constructor(parent, property) { super(); this.parent = parent; this.property = property; } compile() { return `(${this.parent.compile()})["${this.property}"]`; } } Compilable.Dot = Dot; class Property extends Compilable { constructor(prefix, property) { super(); this.prefix = prefix; this.property = property; } static create(prefix, property) { return new Property(prefix, property); } static create_event(eventCtor, property) { return new Property("event", property); } compile() { return `${this.prefix}["${this.property}"]`; } } Compilable.Property = Property; class CategoryProperty extends Compilable { constructor(prefix, property, categories) { super(); this.prefix = prefix; this.property = property; this.categories = categories; for (let name in categories) { this[name] = this.equals(categories[name]); } } static create(prefix, property, categories) { let prop = new CategoryProperty(prefix, property, categories); return prop; } static create_event(obj, property, categories) { let prop = new CategoryProperty("event", property, categories); return prop; } compile() { return `${this.prefix}["${this.property}"]`; } } Compilable.CategoryProperty = CategoryProperty; })(Compilable = exports.Compilable || (exports.Compilable = {})); //---------------------------------------------------------------------- // Public API //---------------------------------------------------------------------- /** Trigger the handler with the given properties when this event fires. */ exports.handle = EventBinder.handle; exports.get = { elem: (prop) => { return Compilable.Property.create("elem", prop); }, target: { value: Compilable.Property.create("target", "value"), content: Compilable.Property.create("target", "textContent"), selected: new Compilable.Computed(`[].slice.apply($1).map((opt) => opt.value)`, Compilable.Property.create("target", "selectedOptions")), bounds: new Compilable.Computed(`target.getBoundingClientRect()`) }, node: new Compilable.Computed(`event.target`), event: new Compilable.Computed(`event`), mouse: { button: Compilable.CategoryProperty.create_event(MouseEvent, "button", { left: 0, middle: 1, right: 2 }), x: Compilable.Property.create_event(MouseEvent, "clientX"), y: Compilable.Property.create_event(MouseEvent, "clientY"), wheel: { delta_mode: Compilable.Property.create_event(WheelEvent, "deltaMode"), delta_x: Compilable.Property.create_event(WheelEvent, "deltaX"), delta_y: Compilable.Property.create_event(WheelEvent, "deltaY"), delta_z: Compilable.Property.create_event(WheelEvent, "deltaZ") } }, keyboard: { key: Compilable.Property.create_event(KeyboardEvent, "key"), mod: (mod) => new Compilable.Computed(`event.getModifierState("${mod}")`) }, do: { target: { clear: new Compilable.Snippet(`target.value = ""`), style: (property, value) => new Compilable.Snippet(`target.style.setProperty(${embed(property)}, ${embed(value)})`) } } }; /** Registry for compiled event handlers (automatically maintained when compiled). */ exports.handlers = {}; //# sourceMappingURL=catalyst.js.map