UNPKG

componentjs-mvc

Version:

Abstract Classes for Model-View-Controller Component Roles

216 lines (206 loc) 8.3 kB
/* ** ComponentJS-MVC -- Model-View-Controller Component Roles ** Copyright (c) 2016-2018 Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* helper function for finding the model of a controller */ const modelOf = (comp) => { if (comp.marked("controller")) { let children = comp.children() for (let i = 0; i < children.length; i++) if (children[i].marked("model")) return children[i] } return comp } export default function (MVC) { /* abstract class for components of any role */ return class Component { /* expose reference to ComponentJS main function */ cs (...args) { return MVC.ComponentJS(...args) } /* establish/demolish/lookup own child/descendant components */ establish (...args) { if (!this.__ComponentJS_MVC_comp) this.__ComponentJS_MVC_comp = {} if (!(typeof args[0] === "string" && typeof args[1] === "string")) args.unshift("") let [ anchor, tree, classes, autoincrease = true, autodecrease = false ] = args if (!(typeof classes === "object" && classes instanceof Array)) classes = [ classes ] let objs = classes.map((Clz) => typeof Clz === "function" ? new Clz() : Clz) let comp = (anchor !== "" ? MVC.ComponentJS(this, anchor) : MVC.ComponentJS(this)) comp.create(tree, ...objs) objs.forEach((obj) => { let comp = MVC.ComponentJS(obj) let id = comp.name() this.__ComponentJS_MVC_comp[id] = obj comp.state_auto_increase(autoincrease) comp.state_auto_decrease(autodecrease) MVC.hook("establish:post-create", "none", { id, comp, obj }) }) return this } demolish (id) { if (!this.__ComponentJS_MVC_comp) this.__ComponentJS_MVC_comp = {} if (id === undefined) Object.keys(this.__ComponentJS_MVC_comp).forEach((id) => this.unconstruct(id)) else { if (this.__ComponentJS_MVC_comp[id] === undefined) throw new Error(`demolish: no such component "${id}"`) let obj = this.__ComponentJS_MVC_comp[id] let comp = MVC.ComponentJS(obj) MVC.hook("demolish:pre-destroy", "none", { id, comp, obj }) comp.destroy() delete this.__ComponentJS_MVC_comp[id] } return this } my (id) { if (!this.__ComponentJS_MVC_comp) this.__ComponentJS_MVC_comp = {} if (this.__ComponentJS_MVC_comp[id] === undefined) throw new Error(`my: no such component "${id}"`) return this.__ComponentJS_MVC_comp[id] } exists (id) { return this.__ComponentJS_MVC_comp && this.__ComponentJS_MVC_comp[id] } /* 1:1 pass-through state-related methods */ state (...args) { return MVC.ComponentJS(this).state(...args) } guard (...args) { return MVC.ComponentJS(this).guard(...args) } await (...args) { return MVC.ComponentJS(this).await(...args) } /* wrap spool-related methods */ spool (func, options = {}) { let opts = Object.assign({}, { name: MVC.ComponentJS(this).state(), ctx: this }, options, { func: func }) MVC.ComponentJS(this).spool(opts) return this } /* wrap model-related methods */ observe (name, func, options = {}) { if (!(typeof name === "object" && name instanceof Array)) name = [ name ] name.forEach((name) => { let opts = Object.assign({}, { spool: MVC.ComponentJS(this).state(), noevent: true }, options, { name: name, func: (...args) => { /* wrap to ensure return value is undefined or an "async" callback would update the model field */ func(...args) } }) modelOf(MVC.ComponentJS(this)).observe(opts) }) return this } value (...args) { return modelOf(MVC.ComponentJS(this)).value(...args) } touch (...args) { modelOf(MVC.ComponentJS(this)).touch(...args) return this } /* wrap event-related methods */ subscribe (name, func, options = {}) { if (!(typeof name === "object" && name instanceof Array)) name = [ name ] name.forEach((name) => { let opts = Object.assign({}, { spool: MVC.ComponentJS(this).state(), noevent: true, capturing: false, spreading: false, bubbling: true }, options, { name: name, func: func }) return MVC.ComponentJS(this).subscribe(opts) }) return this } publish (name, args, options = {}) { let argv = args if (!(typeof args === "object" && args instanceof Array)) argv = [ args ] let opts = Object.assign({}, { directresult: true, capturing: true, spreading: false, bubbling: true }, options, { name: name, args: argv }) return MVC.ComponentJS(this).publish(opts) } /* wrap service-related methods */ register (name, func, options = {}) { if (!(typeof name === "object" && name instanceof Array)) name = [ name ] name.forEach((name) => { let opts = Object.assign({}, { spool: MVC.ComponentJS(this).state(), capturing: false, spreading: false, bubbling: true }, options, { name: name, func: func }) return MVC.ComponentJS(this).register(opts) }) return this } call (name, args, options = {}) { let argv = args if (!(typeof args === "object" && args instanceof Array)) argv = [ args ] let opts = Object.assign({}, { capturing: true, spreading: false, bubbling: true }, options, { name: name, args: argv }) return MVC.ComponentJS(this).call(opts) } } }