Don P: Evolutionszyklen in der Sprachanwendung

Beitrag lesen

Hallo PeterS,

Inzwischen können die sich super verständigen, indem sie einfach
Events abschicken und empfangen, und plötzlich ist alles so einfach
wie nie zuvor, ein Quantensprung in der Programmierung!
...

Lass doch mal was von Deinem code sehen. Ich bin ehrlich interessiert.

Hmm, soll ich? Ist vielleicht noch nicht ganz ausgereift... möchte mich ja nicht blamieren, aber was solls. Hier der Code, den ich z.Zt. für eingewisses Projekt benutze:

  
  
window.onload = function () {  
  
    // common variables:  
    var tbl={}, r={}, g=window, d=g.document,  
  
        el=function(id){return d.getElementById(id);},  
  
        hf = (function () { // helper functions  
  
            var forOwn   = function (o,f) {for(var p in o){if(o.hasOwnProperty(p)){f(p,o[p]);}}},  
                augment  = function (t,o) {forOwn(o,function(p,v){t[p]=v;});return t;},  
                arrayOf  = function (c)   {for(var a=[],i=0,e=c[i];e;e=c[++i]){a.push(e);}return a;};  
  
            return { forOwn:forOwn, augment:augment, arrayOf:arrayOf, };  
        })(),  
  
        ef = (function(){ // event functions  
  
            var queue  = [],  
  
                fireEvents  = function(evtType,data,restore){ // event dispatcher  
  
                    var restoreElts = 0, targets = evtType && r.ui.events[evtType].targets,  
                        obj = function(evtType,targets,data){return {evtType:evtType,targets:targets,data:data};},  
                        prm = function(obj){evtType=obj.evtType;targets=obj.targets;data=obj.data;};  
  
                    if(!restore){  
  
                        data = data || (evtType && r.ui.events[evtType].data);  
                        if (!evtType) { prm(ef.queue[0]); } else if (ef.queue.push(obj(evtType,targets,data))&&ef.queue.length>1){return;};  
  
                        for (var i=0,elt=targets[i]; elt; elt=targets[++i]){(elt.off=!!elt.disabled)&&(++restoreElts)&&(elt.disabled=false)};  
  
                        if(restoreElts){g.setTimeout(function(){ef.fireEvents(evtType,data,true);},1);return;} prm(ef.queue.shift());  
  
                    } else { ef.queue[0] && prm(ef.queue.shift()); }  
  
                    data && hf.augment(r.ui.events[evtType].dispatch, data);  
                    for(var i=0,elt=targets[i];elt;elt=targets[++i]) { elt.dispatchEvent(r.ui.events[evtType].dispatch)&&(elt.disabled=!!elt.off);}  
  
                    ef.queue.length && ef.fireEvents();  
                 },  
  
                evTarget = function(e){return (e=e||event)&&(e.target||e.srcElement);},  
  
            return { queue:queue, fireEvents:fireEvents, evTarget:evTarget, };  
  
        })(); // var  
  
    r = ({ // root object  
  
        ui: ({  
  
            elements:{},  
  
            handlers: {  
  
                btnSimStart: { title: 'Simulation starten',  
  
                    onclick:  function () {this.blur(); /* code... */},  
  
                    init: function() {/* some code */},  
  
                    onNoTable: undefined, onPermInput:undefined, onPermInsert: undefined,  
                },  
  
                btnSimStop: { title: 'Simulation stoppen',  
  
                    onclick:  function () {this.blur(); /* code...*/},  
  
                    onNoTable:undefined, onPermInput:undefined, onPermInsert: undefined,  
                },  
            },  
  
            events: {  
  
                onNoTable:    { dispatch:{}, targets: [], run: function(e){ef.evTarget(e).disable(true ,e);} },  
                onIsTable:    { dispatch:{}, targets: [], run: function(e){ef.evTarget(e).disable(false,e);} },  
                onNewTable:   { dispatch:{}, targets: [], run: undefined, data:{/* something */} },  
            },  
  
            common: {  
  
                disable: function(bool,e){e&&e.stopPropagation();return (this.disabled=this.off=bool);},  
                display: function(bool,e){e&&e.stopPropagation();if(this.style)return !(this.style.display=bool?'':'none');},  
                visible: function(bool,e){e&&e.stopPropagation();if(this.style)return (this.style.visibility=bool?'visible':'hidden');},  
            },  
  
            initIDchildren: function (id, tagName) {  
  
                var elt, evtList=this.events, tags=el(id).getElementsByTagName(tagName);  
  
                for (var evtType in evtList){ //create & initialize dispatchable events and make them properties of this.events:  
  
                    evtList[evtType].dispatch=(  
  
                        function(evtType){var evtObj=d.createEvent("Events");evtObj.initEvent(evtType,true,true);return evtObj;}  
  
                    )(evtType);  
                }  
  
                for (var i=0,elt=tags[i],id=elt.id; elt; (elt=tags[++i])&&(id=elt.id) ) {  
  
                    if(!id)continue;  
  
                    hf.augment(elt, this.common); (id in this.handlers) && hf.augment(elt,this.handlers[id]);  
  
                    for (var evtType in evtList){  
  
                        if(evtType in elt){elt.addEventListener(evtType,(elt[evtType]||evtList[evtType].run),false); evtList[evtType].targets.push(elt);}  
                    }  
                    elt.init && elt.init();  
                    this.elements[id]=elt;  
                }  
  
                this.elements.events = evtList; // for fireEvent()  
                return this.elements;  
            },  
  
        }).initIDchildren('someId','*'), //ui  
  
        init: function () {  
  
            // possibly more initialization here...  
            return this;  
        }  
  
    }).init(); //r (root)  
  
    //alert('dispatching onNoTable...');  
    ef.fireEvents('onNoTable');  // Anfangszustand einstellen  
};  

Viel kürzer ging's nicht, sorry. Habe hoffentlich nicht zu viel rausgenommen.

Es läuft so: onload werden mit initIDchildren('someId','*') all jene DOM-Elemente mit Funktionalität versehen, die unterhalb des Knotens id="someId" liegen und ein id-Attribut haben.
Jedes erhält die Methoden vom common-Objekt und außerdem alle Methoden und Eigenschaften, die im Objekt handler-Objekt unter ihrer ID notiert sind.

Mögliche Events werden im events-Objekt definiert.
Dort werden sie anfangs automatisch vorbereitet (ihr dispatch-Objekt erzeugt) und unter "run" kann ein Defaulthandler notiert werden, der dann onEvent auszuführen ist.
Im handler-Objekt unter der jeweiligen id kann man auch einen speziellen onBlaBla-handler für jeden Event notieren, der dann Vorrang hat. Falls dort "undefined" steht, gilt der default-handler onBlaBla vom events-Objekt.
Schließlich kann man in events auch ein data-Objekt notieren und/oder mit ef.fireEvents('onBlaBla',{/* dies und das */}) direkt mitgeben. Das Objekt wird dann mit dem Event an alle intressierten Listener weitergereicht.

Da es mir zunächst hauptsächlich darum ging, dass sich Buttons über solche Events aktivieren/deaktivieren lassen, stand ich vor einem großen Problem: Ein input/button oder was immer mit dem Attribut "disabled" reagiert leider auf gar nichts mehr, auch der schönste Event geht ihm sozusagen am A... vorbei.
Der Dispatcher ef.fireEvents ist daher etwas umfangreich geworden: Deaktivierte Elemente werden zunächst aktiviert und der Event erst nach einem kurzen timeout (muss leider sein) gefeuert, dann ggf. wieder deaktiviert (falls nicht gerade der Event zum Aktivieren diente). Damit sich nacheinander gefeuerte Events dabei nicht in die Quere kommen, landen sie ggf. erst in einer Warteschlange.

Fazit: Von hinten durch die Brust ins Auge - aber immerhin Volltreffer ;-)

Das Ganze funktioniert bis jetzt richtig gut. Eigentlich müssten auch noch Vereinfachungen möglich sein, v.a. mit dem events-Objekt bin ich nicht ganz zufrieden. Das sollte wohl eher in ins ef-Objekt verschoben werden...

Gruß, Don P