Rocksolid Light

Welcome to RetroBBS

mail  files  register  newsreader  groups  login

Message-ID:  

PURGE COMPLETE.


devel / comp.lang.javascript / baby steps

SubjectAuthor
* baby stepsluserdroog
`* Re: baby stepsMichael Haufe (TNO)
 +* Re: baby stepsJulio Di Egidio
 |`* Re: baby stepsMichael Haufe (TNO)
 | `- Re: baby stepsJulio Di Egidio
 `- Re: baby stepsluserdroog

1
baby steps

<edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17545&group=comp.lang.javascript#17545

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a05:620a:a50:: with SMTP id j16mr765323qka.766.1639718293349;
Thu, 16 Dec 2021 21:18:13 -0800 (PST)
X-Received: by 2002:a05:6808:14c2:: with SMTP id f2mr852511oiw.154.1639718293024;
Thu, 16 Dec 2021 21:18:13 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Thu, 16 Dec 2021 21:18:12 -0800 (PST)
Injection-Info: google-groups.googlegroups.com; posting-host=97.87.183.68; posting-account=G1KGwgkAAAAyw4z0LxHH0fja6wAbo7Cz
NNTP-Posting-Host: 97.87.183.68
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>
Subject: baby steps
From: luser.droog@gmail.com (luserdroog)
Injection-Date: Fri, 17 Dec 2021 05:18:13 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 121
 by: luserdroog - Fri, 17 Dec 2021 05:18 UTC

I've read through Michael Haufe's example a few more times, and
re-read the old article by Peter Michaux. And through all of it, I like
the idea of structuring stuff. The major divisions of responsibility
make sense. And to a certain extent, the lines of communication
make sense. But in all of these implementations there feels (to me)
like a whole lotta stuff I don't need. Or don't need yet. Or don't
understand. Or something.

So I've tried to build everything up as simply as I could manage.
I don't need to dynamically change the list of observers beyond
adding at least one: so it's just one one-liner function; etc. I did run
in to one weird corner in trying to make a Button out of a View.
It doesn't really need a model. But I have to give it some kind of
model because the constructor needs to add itself as an observer
to *something*.

But this feels like progress on the major front. It's a little sloppy
and free-for-all inside the objects, but everything is *inside* the
objects! This code doesn't actually draw anything except the
buttons. And the buttons don't visibly do anything. But importantly
they do all of this nothing *without errors*.

class Model {
constructor(){ this._observers = []; }
observedBy( observer ){ this._observers.push( observer ); return this; }
notify( event ){ this._observers.forEach(o=>o.update(event)); }
}

class View {
constructor( model, container ){
this._model = model.observedBy( this );
this._container = container;
this._root = this.initRoot();
}
initRoot(){ return document.createElement("div"); }
render(){ this._container.appendChild( this._root ); }
update( event ){}
}

class Controller {
constructor(){ }
}

class Notes extends Model {
constructor(){
super();
this.data = [];
}
sync(){ this.notify({data:this.data}); }
clear(){
this.data = [];
this.sync();
}
add( notes ){
this.data.push(notes);
this.sync();
}
toggle( y, x ){
if( this.data[ y ].includes( x ) ){
this.data[ y ] = this.data[ y ].filter( n=> n!=x );
} else {
this.data[ y ].push( x );
}
this.sync();
}
}

class PianoRollView extends View {
constructor( model, container ){
super( model, container );
}
initRoot(){ return document.createElement("table"); }
update( event ){ this.draw( event.data ); }
draw( notes ){ }
}

class PianoPlayer extends View {
constructor( model, container ){
super( model, container );
}
play( notes ){ }
}

class Button extends View {
constructor( model, label, action, container ){
super( model, container );
this._root.textContent = label;
this._root.onclick = action;
this.render();
}
initRoot(){ return document.createElement("button"); }
}

class PianoRoll extends Controller {
constructor( container ){
super();
this.model = new Notes();
this.view = new PianoRollView( this.model, container );
this.player = new PianoPlayer( this.model, container );

var nullModel = new Model();
var model = this.model;
var player = this.player;
this.add = new Button( nullModel,
'add row',
()=>model.add([]),
container );
this.play = new Button( nullModel,
'play',
()=>player.play(model.data),
container );
this.clear = new Button( nullModel,
'clear',
()=>model.clear(),
container );
}
}

const piano = new PianoRoll( document.body );

Re: baby steps

<a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17556&group=comp.lang.javascript#17556

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a37:8d86:: with SMTP id p128mr5542772qkd.706.1639858462520;
Sat, 18 Dec 2021 12:14:22 -0800 (PST)
X-Received: by 2002:a54:4191:: with SMTP id 17mr12425595oiy.50.1639858462194;
Sat, 18 Dec 2021 12:14:22 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Sat, 18 Dec 2021 12:14:21 -0800 (PST)
In-Reply-To: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2603:6000:8900:6915:e86c:1740:3ab0:b9f;
posting-account=hYRygAoAAABkmvJVmPilz9Q1TOjgPQAq
NNTP-Posting-Host: 2603:6000:8900:6915:e86c:1740:3ab0:b9f
References: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>
Subject: Re: baby steps
From: tno@thenewobjective.com (Michael Haufe (TNO))
Injection-Date: Sat, 18 Dec 2021 20:14:22 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 86
 by: Michael Haufe (TNO) - Sat, 18 Dec 2021 20:14 UTC

On Thursday, December 16, 2021 at 11:18:18 PM UTC-6, luser...@gmail.com wrote:
> I've read through Michael Haufe's example a few more times, and
> re-read the old article by Peter Michaux. And through all of it, I like
> the idea of structuring stuff. The major divisions of responsibility
> make sense. And to a certain extent, the lines of communication
> make sense. But in all of these implementations there feels (to me)
> like a whole lotta stuff I don't need. Or don't need yet. Or don't
> understand. Or something.

Most likely "don't need yet". MVC is overkill for simplistic pages or things don't really rise to the level of an application.

Recall that Peter Michaux and I both presented an MVC library in ~100 lines.
With such examples they should be treated as impressionistic and incomplete.

> So I've tried to build everything up as simply as I could manage.
> I don't need to dynamically change the list of observers beyond
> adding at least one: so it's just one one-liner function; etc. I did run
> in to one weird corner in trying to make a Button out of a View.
> It doesn't really need a model. But I have to give it some kind of
> model because the constructor needs to add itself as an observer
> to *something*.

You don't need a model. It could be just the Controller and View.

> But this feels like progress on the major front. It's a little sloppy
> and free-for-all inside the objects, but everything is *inside* the
> objects! This code doesn't actually draw anything except the
> buttons. And the buttons don't visibly do anything. But importantly
> they do all of this nothing *without errors*.

I'm glad you've made progress with my back-of-napkin example.
There are plenty of places that could be improved with naming
conventions, refactoring, utilities, etc.

One thing I'd consider is to align the obervers with the DOM.
Since the DOM uses `addEventListener` is might be more
clear to rename `observedBy` to something similar.

`container` might be more intuitive being renamed to `parent`

Should the Notes be responsible for toggling? Would that make more sense in the owning controller?

Again on the event management side. Note that the DOM's 'addEventListener' can accept an object as its second parameter.
This could be a good opportunity to simplify all of the event management in general

An example:

class Observable {
// ...
handleEvent(e) {
let name = e.type;
if(this[name]) this[name](e);
}
}

class View extends Observable {
// ...
}

class MyView extends View {
constructor() {
super()
// ...
this._rootElement.addEventListener('click', this)
}

onclick(e) {
// do something with the click event
}
}

Playing around with Model a little should enable a common approach for both.
One challenge being that the DOM elements have a private list of observers
so some duplication of this list would exist.

You can see more of this `handleEvent` usage in another example I made for a
previous discussion in the group:

(note: NOT an MVC approach. Just look at the handleEvent usage)
<https://codepen.io/mlhaufe/pen/RwKqzvr>

Thinking about this as I write it: Maybe a weird hack could be
to have a dummy HTML element in the model just to take advantage
of its event code. So instead of notifyObservers, you raise the CustomEvent
against that dummy element so the same `handleEvent` method is called.

If I have time this weekend, I'll play with the idea.

Re: baby steps

<9aba3a49-2bda-4581-94dc-3c722c61616fn@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17557&group=comp.lang.javascript#17557

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a05:620a:2996:: with SMTP id r22mr5657675qkp.485.1639864605933;
Sat, 18 Dec 2021 13:56:45 -0800 (PST)
X-Received: by 2002:a9d:6ac7:: with SMTP id m7mr6558058otq.306.1639864605570;
Sat, 18 Dec 2021 13:56:45 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Sat, 18 Dec 2021 13:56:45 -0800 (PST)
In-Reply-To: <a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=93.41.100.218; posting-account=F3H0JAgAAADcYVukktnHx7hFG5stjWse
NNTP-Posting-Host: 93.41.100.218
References: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com> <a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <9aba3a49-2bda-4581-94dc-3c722c61616fn@googlegroups.com>
Subject: Re: baby steps
From: julio@diegidio.name (Julio Di Egidio)
Injection-Date: Sat, 18 Dec 2021 21:56:45 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 26
 by: Julio Di Egidio - Sat, 18 Dec 2021 21:56 UTC

On Saturday, 18 December 2021 at 21:14:27 UTC+1, Michael Haufe (TNO) wrote:

> You don't need a model. It could be just the Controller and View.

Just the opposite is the case: if you don't need anything, you need a model, otherwise it's a view-model, otherwise it's a model-view-view-mdel, otherwise it's full fledged MVC.

> One thing I'd consider is to align the obervers with the DOM.

Again, on the contrary: observables are more fundamental and more ubiquitous than any DOM.

> One challenge being that the DOM elements have a private list of observers

Once challenge is to avoid any such coupling, and that's where one wants a view.

> Thinking about this as I write it: Maybe a weird hack could be
> to have a dummy HTML element in the model just to take advantage
> of its event code.

From bad to worse: that would very ugly, not just unnecessary and in fact upside down.

> If I have time this weekend, I'll play with the idea.

Of all the ideas...

HTH,

Julio

Re: baby steps

<5a029ddc-e2a2-499e-a9d2-d917a4c3a724n@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17558&group=comp.lang.javascript#17558

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a05:622a:1112:: with SMTP id e18mr7678035qty.226.1639868975454;
Sat, 18 Dec 2021 15:09:35 -0800 (PST)
X-Received: by 2002:a05:6808:1aa8:: with SMTP id bm40mr12207903oib.38.1639868975192;
Sat, 18 Dec 2021 15:09:35 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Sat, 18 Dec 2021 15:09:34 -0800 (PST)
In-Reply-To: <9aba3a49-2bda-4581-94dc-3c722c61616fn@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=2603:6000:8900:6915:2862:230e:c576:f496;
posting-account=hYRygAoAAABkmvJVmPilz9Q1TOjgPQAq
NNTP-Posting-Host: 2603:6000:8900:6915:2862:230e:c576:f496
References: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>
<a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com> <9aba3a49-2bda-4581-94dc-3c722c61616fn@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <5a029ddc-e2a2-499e-a9d2-d917a4c3a724n@googlegroups.com>
Subject: Re: baby steps
From: tno@thenewobjective.com (Michael Haufe (TNO))
Injection-Date: Sat, 18 Dec 2021 23:09:35 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 24
 by: Michael Haufe (TNO) - Sat, 18 Dec 2021 23:09 UTC

On Saturday, December 18, 2021 at 3:56:49 PM UTC-6, ju...@diegidio.name wrote:
> On Saturday, 18 December 2021 at 21:14:27 UTC+1, Michael Haufe (TNO) wrote:
>
> > You don't need a model. It could be just the Controller and View.
> Just the opposite is the case: if you don't need anything, you need a model, otherwise it's a view-model, otherwise it's a model-view-view-mdel, otherwise it's full fledged MVC.

We're referring to a button. A model gives you nothing there.

> > One thing I'd consider is to align the obervers with the DOM.
> Again, on the contrary: observables are more fundamental and more ubiquitous than any DOM.

It's not contrary, it's orthogonal. Right now he has to manage observers himself in the model, but the DOM
is managing them in the view. There are two different approaches to managing these right now. These should
be normalized. Since you can't avoid using the DOM here one might as well bring the other approach into alignment.

> > One challenge being that the DOM elements have a private list of observers
> Once challenge is to avoid any such coupling, and that's where one wants a view.
> > Thinking about this as I write it: Maybe a weird hack could be
> > to have a dummy HTML element in the model just to take advantage
> > of its event code.
> From bad to worse: that would very ugly, not just unnecessary and in fact upside down.
> > If I have time this weekend, I'll play with the idea.
> Of all the ideas...

You're free to present something better.

Re: baby steps

<8a68d9bd-4f3b-4238-948c-e19f18338cf1n@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17560&group=comp.lang.javascript#17560

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a05:622a:120e:: with SMTP id y14mr8624865qtx.671.1639910420381;
Sun, 19 Dec 2021 02:40:20 -0800 (PST)
X-Received: by 2002:a05:6808:1aa8:: with SMTP id bm40mr13303573oib.38.1639910420111;
Sun, 19 Dec 2021 02:40:20 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Sun, 19 Dec 2021 02:40:19 -0800 (PST)
In-Reply-To: <5a029ddc-e2a2-499e-a9d2-d917a4c3a724n@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=93.41.100.218; posting-account=F3H0JAgAAADcYVukktnHx7hFG5stjWse
NNTP-Posting-Host: 93.41.100.218
References: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com>
<a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com> <9aba3a49-2bda-4581-94dc-3c722c61616fn@googlegroups.com>
<5a029ddc-e2a2-499e-a9d2-d917a4c3a724n@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <8a68d9bd-4f3b-4238-948c-e19f18338cf1n@googlegroups.com>
Subject: Re: baby steps
From: julio@diegidio.name (Julio Di Egidio)
Injection-Date: Sun, 19 Dec 2021 10:40:20 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 26
 by: Julio Di Egidio - Sun, 19 Dec 2021 10:40 UTC

On Sunday, 19 December 2021 at 00:09:39 UTC+1, Michael Haufe (TNO) wrote:
> On Saturday, December 18, 2021 at 3:56:49 PM UTC-6, ju...@diegidio.name wrote:
> > On Saturday, 18 December 2021 at 21:14:27 UTC+1, Michael Haufe (TNO) wrote:
> >
> > > You don't need a model. It could be just the Controller and View.
> > Just the opposite is the case: if you don't need anything, you need a model, otherwise it's a view-model, otherwise it's a model-view-view-mdel, otherwise it's full fledged MVC.
> We're referring to a button. A model gives you nothing there.
> > > One thing I'd consider is to align the obervers with the DOM.
> > Again, on the contrary: observables are more fundamental and more ubiquitous than any DOM.
> It's not contrary, it's orthogonal. Right now he has to manage observers himself in the model, but the DOM
> is managing them in the view. There are two different approaches to managing these right now. These should
> be normalized. Since you can't avoid using the DOM here one might as well bring the other approach into alignment.
> > > One challenge being that the DOM elements have a private list of observers
> > Once challenge is to avoid any such coupling, and that's where one wants a view.
> > > Thinking about this as I write it: Maybe a weird hack could be
> > > to have a dummy HTML element in the model just to take advantage
> > > of its event code.
> > From bad to worse: that would very ugly, not just unnecessary and in fact upside down.
> > > If I have time this weekend, I'll play with the idea.
> > Of all the ideas...
> You're free to present something better.

As much as you fraudulent spammers are free to get the fuck out of here.

*Plonk*

Julio

Re: baby steps

<4c40a20d-75c6-4832-a873-8fc55335cd6cn@googlegroups.com>

  copy mid

https://www.rocksolidbbs.com/devel/article-flat.php?id=17561&group=comp.lang.javascript#17561

  copy link   Newsgroups: comp.lang.javascript
X-Received: by 2002:a05:620a:2a05:: with SMTP id o5mr2208215qkp.527.1639937134367;
Sun, 19 Dec 2021 10:05:34 -0800 (PST)
X-Received: by 2002:a05:6830:1008:: with SMTP id a8mr9107288otp.373.1639937133985;
Sun, 19 Dec 2021 10:05:33 -0800 (PST)
Path: i2pn2.org!i2pn.org!weretis.net!feeder6.news.weretis.net!news.misty.com!border2.nntp.dca1.giganews.com!nntp.giganews.com!news-out.google.com!nntp.google.com!postnews.google.com!google-groups.googlegroups.com!not-for-mail
Newsgroups: comp.lang.javascript
Date: Sun, 19 Dec 2021 10:05:33 -0800 (PST)
In-Reply-To: <a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>
Injection-Info: google-groups.googlegroups.com; posting-host=97.87.183.68; posting-account=G1KGwgkAAAAyw4z0LxHH0fja6wAbo7Cz
NNTP-Posting-Host: 97.87.183.68
References: <edcb3539-1b18-44c0-a1d5-7dae6a760bfen@googlegroups.com> <a3b8656e-9d5f-41d9-ad19-eb1f6b8fae9an@googlegroups.com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <4c40a20d-75c6-4832-a873-8fc55335cd6cn@googlegroups.com>
Subject: Re: baby steps
From: luser.droog@gmail.com (luserdroog)
Injection-Date: Sun, 19 Dec 2021 18:05:34 +0000
Content-Type: text/plain; charset="UTF-8"
Lines: 243
 by: luserdroog - Sun, 19 Dec 2021 18:05 UTC

On Saturday, December 18, 2021 at 2:14:27 PM UTC-6, Michael Haufe (TNO) wrote:
> On Thursday, December 16, 2021 at 11:18:18 PM UTC-6, luser...@gmail.com wrote:
> > I've read through Michael Haufe's example a few more times, and
> > re-read the old article by Peter Michaux. And through all of it, I like
> > the idea of structuring stuff. The major divisions of responsibility
> > make sense. And to a certain extent, the lines of communication
> > make sense. But in all of these implementations there feels (to me)
> > like a whole lotta stuff I don't need. Or don't need yet. Or don't
> > understand. Or something.
> Most likely "don't need yet". MVC is overkill for simplistic pages or things don't really rise to the level of an application.
>
> Recall that Peter Michaux and I both presented an MVC library in ~100 lines.
> With such examples they should be treated as impressionistic and incomplete.
> > So I've tried to build everything up as simply as I could manage.
> > I don't need to dynamically change the list of observers beyond
> > adding at least one: so it's just one one-liner function; etc. I did run
> > in to one weird corner in trying to make a Button out of a View.
> > It doesn't really need a model. But I have to give it some kind of
> > model because the constructor needs to add itself as an observer
> > to *something*.
> You don't need a model. It could be just the Controller and View.

Hmm. I don't quite follow that. Conceptually yes, but the way I've
implemented it, the constructor for the View superclass expects
a Model (something that implements observedBy()). But I tucked it
away inside the Button constructor now.

> > But this feels like progress on the major front. It's a little sloppy
> > and free-for-all inside the objects, but everything is *inside* the
> > objects! This code doesn't actually draw anything except the
> > buttons. And the buttons don't visibly do anything. But importantly
> > they do all of this nothing *without errors*.
> I'm glad you've made progress with my back-of-napkin example.
> There are plenty of places that could be improved with naming
> conventions, refactoring, utilities, etc.
>
> One thing I'd consider is to align the obervers with the DOM.
> Since the DOM uses `addEventListener` is might be more
> clear to rename `observedBy` to something similar.

Worth considering. I like the "adjective" name as being more functional
than a "verb" name, but maybe that's not the most important factor here.

> `container` might be more intuitive being renamed to `parent`

Yeah, that's fewer letters too.

> Should the Notes be responsible for toggling? Would that make more sense in the owning controller?

Hmm. Maybe. The click event that initiates it should probably route
through the controller. But I strongly see it as an operation on the model
which should be part of the model's interface.

> Again on the event management side. Note that the DOM's 'addEventListener' can accept an object as its second parameter.
> This could be a good opportunity to simplify all of the event management in general
>
> An example:
[snip interesting event handling example]

I finished fleshing out the code to the same level of functionality as the previous
It relies on the scales and running_sum() functions from my other thread in a
file called fim.js. Plus an additional scale
const pentatonic = [ 2, 3, 2, 2, 3 ]; // c#..d#..f#..g#..a#..(c#)
used for painting the black keys on the keyboard.

The way I have it set up, the PianorollView builds all the functions
that call model.toggle(). And the View doesn't have knowledge of the
Controller, but it does have a pointer to the Model. The Controller
includes the others by composition rather than inheritance.

<html><!-- fim.html -->
<head>
<meta charset="utf-8" />
<style>
.pianoroll table { table-layout: fixed;
border-collapse: collapse;
border: 1px solid;
cursor: pointer;
}
.pianoroll td { border: 1px solid;
padding: 0; }
.pianoroll tr.heavy td.black:hover { color: red; background-color: red; }
.pianoroll td.black:hover { color: blue; background-color: blue; }
.pianoroll td:hover { color: red; background-color: red; }
.heavy { border: 2.5px solid; }
.black { background-color: black; }
</style>
</head>
<body>
</body>
<script src="fim.js"></script> <!--scales and chords-->
<script src="mvc.js"></script> <!--mvc piano roll-->
</html>

// mvc.js

class Model {
constructor(){ this._observers = []; }
observedBy( observer ){ this._observers.push( observer ); return this; }
notify( event ){ this._observers.forEach(o=>o.update(event)); }
}

class View {
constructor( model, container ){
this._model = model.observedBy( this );
this._container = container;
this._root = this.initRoot();
}
initRoot(){ return document.createElement("div"); }
render(){ this._container.appendChild( this._root ); }
update( event ){}
}

class Controller {
constructor(){ }
}

class Notes extends Model {
constructor(){
super();
this.data = [];
}
sync(){ this.notify({data:this.data}); }
clear(){
this.data = [];
this.sync();
}
add( notes ){
this.data.push(notes);
this.sync();
}
toggle( y, x ){
if( this.data[ y ].includes( x ) ){
this.data[ y ] = this.data[ y ].filter( n=> n!=x );
} else {
this.data[ y ].push( x );
}
this.sync();
}
}

class PianoRollView extends View {
constructor( model, container ){
super( model, container );
}
initRoot(){ return document.createElement("table"); }
update( event ){ this.draw( event.data ); }
draw( notes ){
var table = document.createElement("table");
var row = this.row;
var cell = this.cell;
var black = this.black;
var toggle = this.toggle;
var model = this._model;
notes.forEach( function( n, idx ){
table.appendChild( row( n, idx, toggle, model, cell, black ) );
});
table.appendChild( this.keyboard() );
this._container.removeChild( this._root );
this._root = table;
this.render();
}
toggle( model, i, j ){
return ()=> model.toggle( i, j );
}
add( model, i, j ){
return ()=> model.add( [j] );
}
keyboard(){
return this.heavy( this.row( running_sum( repeat( octaves, pentatonic ) )
.map( x=> x+1 ),
0, this.add, this._model, this.cell, this.black ) );
}
row( pitches, idx, action, model, cell, black ){
pitches.sort( (x,y)=> x<y?-1 :x==y?0 :1 );
var tr = document.createElement("tr");
var i = 0;
pitches.forEach( p=>{
for( ; i < p && i < octaves*12; i++ )
tr.appendChild( cell( action, model, idx, i ) );
if( i >= octaves*12 ) return;
tr.appendChild( black( cell( action, model, idx, i++ ) ) );
});
for( ; i < octaves*12; i++ )
tr.appendChild( cell( action, model, idx, i ) );
return tr;
}
black( thing ){ thing.className = 'black'; return thing; }
heavy( thing ){ thing.className = 'heavy'; return thing; }
cell( action, model, i, j ){
var td = document.createElement("td");
var a = document.createElement("a");
td.appendChild( a );
var content = document.createTextNode("\u00A0"); //nbsp
a.appendChild( content );
a.onclick = action( model, i, j );
return td;
}
}

class PianoPlayer extends View {
constructor( model, container ){
super( model, container );
}
play( notes ){ }
}

class Button extends View {
constructor( label, action, container ){
super( new Model(), container );
this._root.textContent = label;
this._root.onclick = action;
this.render();
}
initRoot(){ return document.createElement("button"); }
}

class PianoRoll extends Controller {
constructor( container = document.body ){
super();
var a = document.createElement("div");
a.className = 'pianoroll';
container.appendChild(a);
this.model = new Notes();
this.view = new PianoRollView( this.model, a );
this.view.render();
this.model.sync();
this.player = new PianoPlayer( this.model, a );

var b = document.createElement("div");
container.appendChild(b);
var model = this.model;
var player = this.player;
this.add = new Button( 'add row', ()=>model.add([]), b );
this.play = new Button( 'play', ()=>player.play(model.data), b );
this.clear = new Button( 'clear', ()=>model.clear(), b );
}
}


Click here to read the complete article

devel / comp.lang.javascript / baby steps

1
server_pubkey.txt

rocksolid light 0.9.81
clearnet tor