[Laszlo-dev] For Review: Change 20071105-ptw-V Summary: Ensure deferred events do not recurse

P T Withington ptw at pobox.com
Thu Dec 20 07:28:52 PST 2007


I am going to checkpoint the solution so far, which covers several of  
your test cases, but not this super one.  I need to get some other  
stuff done, but don't want to lose this thread.  Can you file a new  
Jira item with this?

On 2007-12-04, at 15:06 EST, André Bargull wrote:

> I've got another "super" testcase for you, which shows again  
> differences between queued and non-queued delegates...I guess you're  
> starting to hate me...
>
> testcase:
> [code]
> <canvas debug="true" >
>     <class name="test" extends="node" >
>       <attribute name="foo" value="000" type="string"  
> setter="setFoo(foo)" />
>       <attribute name="bar" value="aaa" type="string"  
> setter="setBar(bar)" />
>             <event name="onfoo" />
>       <event name="onbar" />
>             <handler name="onfoo" args="foo" >
>           Debug.write("onfoo with %s", foo);
>           this.setBar(this.bar + foo);
>       </handler>
>             <handler name="onbar" args="bar" >
>           Debug.write("onbar with %s", bar);
>           this.setFoo(this.foo + bar);
>       </handler>
>             <method name="setFoo" args="foo" ><![CDATA[
>           Debug.write("setFoo with %s", foo);
>           this.foo = foo;
>           this.onfoo.sendEvent(foo);
>       ]]></method>
>             <method name="setBar" args="bar" ><![CDATA[
>           Debug.write("setBar with %s", bar);
>           this.bar = bar;
>           this.onbar.sendEvent(bar);
>       ]]></method>
>   </class>
>     <test id="foobar" >
>       <!-- later -->
>       <handler name="oninit" reference="canvas" >
>           Debug.write("-----");
>           this.foo = "";
>           this.bar = "";
>           this.setBar("aaa");
>           this.setFoo("000");
>       </handler>
>   </test>
>  </canvas>
> [/code]
>
>
> i) current output (without your changeset applied):
> setBar with aaa
> setFoo with 000
> onbar with aaa
> setFoo with 000aaa
> onfoo with 000aaa
> setBar with aaa000aaa
> onbar with aaa000aaa
> setFoo with 000aaaaaa000aaa
> onfoo with 000
> setBar with aaa000aaa000
> onbar with aaa000aaa000
> setFoo with 000aaaaaa000aaaaaa000aaa000
> onfoo with 000aaaaaa000aaaaaa000aaa000
> setBar with aaa000aaa000000aaaaaa000aaaaaa000aaa000
> -----
> setBar with aaa
> onbar with aaa
> setFoo with aaa
> onfoo with aaa
> setBar with aaaaaa
> setFoo with 000
> onfoo with 000
> setBar with aaaaaa000
> onbar with aaaaaa000
> setFoo with 000aaaaaa000
>
>
> ii) output with your changeset:
> setBar with aaa
> setFoo with 000
> onbar with aaa
> setFoo with 000aaa
> onfoo with 000aaa
> setBar with aaa000aaa
> onfoo with 000
> setBar with aaa000aaa000
> -----
> setBar with aaa
> onbar with aaa
> setFoo with aaa
> onfoo with aaa
> setBar with aaaaaa
> setFoo with 000
> onfoo with 000
> setBar with aaaaaa000
> onbar with aaaaaa000
> setFoo with 000aaaaaa000
>
>
> iii) output of a prototype of mine (see attachment)
> setBar with aaa
> setFoo with 000
> onbar with aaa
> setFoo with 000aaa
> onfoo with 000aaa
> setBar with aaa000aaa
> onfoo with 000
> setBar with aaa000aaa000
> onbar with aaa000aaa000
> setFoo with 000aaaaaa000aaa000
> -----
> setBar with aaa
> onbar with aaa
> setFoo with aaa
> onfoo with aaa
> setBar with aaaaaa
> setFoo with 000
> onfoo with 000
> setBar with aaaaaa000
> onbar with aaaaaa000
> setFoo with 000aaaaaa000
>
>
> Summary:
> i) => too much function calls (+4)
> ii) => function calls are missing (-2)
> iii) => function calls match
>
> BUT, what do we actually expect for queued delegates?
>
>
> On 11/30/2007 6:41 PM, P T Withington wrote:
>> I updated the change to correctly handle your new test case.  Would  
>> you like to re-review it (http://svn.openlaszlo.org/openlaszlo/patches/20071105-ptw-V.tar 
>> )
>>
>> I 'improved' your test case just a bit to make more sense to me:
>>
>> <canvas debug="true" >
>>  <class name="foo" extends="node" >
>>    <attribute name="bar" value="123" setter="setBar(bar)" />
>>    <method name="setBar" args="bar" >
>>      this.bar = bar;
>>      if (this["onbar"]) {
>>        this.onbar.sendEvent(bar);
>>      }
>>    </method>
>>    <method name="provokeError" >
>>      this.setBar("789");
>>    </method>
>>    <method name="provokeError2" >
>>      this.setBar("xxx");
>>    </method>
>>  </class>
>>  <foo bar="456" >
>>    <handler name="onbar" args="bar">
>>      Debug.write("onbar - 1 - begin with %s", bar);
>>      this.provokeError();
>>      Debug.write("onbar - 1 - end with %s", this.bar);
>>    </handler>
>>    <handler name="onbar" args="bar">
>>      Debug.write("onbar - 2 - begin with %s", bar);
>>      this.provokeError2();
>>      Debug.write("onbar - 2 - end with %s", this.bar);
>>    </handler>
>>    <!-- later -->
>>    <handler name="oninit" reference="canvas" >
>>      Debug.write("-----");
>>      this.setBar("abc");
>>    </handler>
>>  </foo>
>> </canvas>
>>
>> On 2007-11-06, at 19:28 EST, André Bargull wrote:
>>
>>> Approved.
>>>
>>> I've got another testcase which shows a different event-handling  
>>> (comparing queued and non-queued delegates),
>>> even after applying this fix, but I don't know whether it is a bit  
>>> too much constructed:
>>>
>>> <canvas debug="true" >
>>> <class name="foo" extends="node" >
>>>  <attribute name="bar" value="123" setter="setBar(bar)" />
>>>    <method name="setBar" args="bar" >
>>>    this.bar = bar;
>>>    if (this["onbar"]) {
>>>      this.onbar.sendEvent(bar);
>>>    }
>>>  </method>
>>>    <method name="provokeError" >
>>>    this.setBar("789");
>>>  </method>
>>>    <method name="provokeError2" >
>>>    this.setBar("xxx");
>>>  </method>
>>> </class>
>>> <foo bar="456" >
>>>  <handler name="onbar" args="bar">
>>>    Debug.write("onbar - 1 - begin with %s", bar);
>>>    this.provokeError();
>>>    Debug.write("onbar - 1 - end with %s", bar);
>>>  </handler>
>>>    <handler name="onbar" args="bar">
>>>    Debug.write("onbar - 2 - begin with %s", bar);
>>>    this.provokeError2();
>>>    Debug.write("onbar - 2 - end with %s", bar);
>>>  </handler>
>>>    <!-- later -->
>>>  <handler name="oninit" reference="canvas" >
>>>    Debug.write("-----");
>>>    this.setBar("abc");
>>>  </handler>
>>> </foo>
>>> </canvas>
>>>
>>> On 11/7/2007 12:23 AM, P T Withington wrote:
>>>> Change 20071105-ptw-V by ptw at dueling-banjos.local on 2007-11-05  
>>>> 15:10:35 EST
>>>>  in /Users/ptw/OpenLaszlo/ringding-2
>>>>  for http://svn.openlaszlo.org/openlaszlo/trunk
>>>>
>>>> Summary: Ensure deferred events do not recurse
>>>>
>>>> Bugs Fixed:
>>>> LPP-4950 'LzDelegate single-execution mechanism does not work for  
>>>> queued delegates'
>>>>
>>>> Technical Reviewer: hminksy (pending)
>>>> QA Reviewer: a.bargull at intensis.de (pending)
>>>>
>>>> Tests:
>>>>  Test case from bug no longer shows the event recursing.
>>>>
>>>> Files:
>>>> M      WEB-INF/lps/lfc/events/LaszloEvents.lzs
>>>>
>>>>
>>>> Changeset: http://svn.openlaszlo.org/openlaszlo/patches/20071105-ptw-V.tar
>>>>
>>
>>
>
> /**
>  *
>  * @copyright Copyright 2001-2007 Laszlo Systems, Inc.  All Rights  
> Reserved.
>  *            Use is subject to license terms.
>  *
>  * @affects lzevent lzdelegate
>  * @topic LFC
>  * @subtopic Events
>  * @access public
>  */
>
> /**
>  * <p>
>  * A delegate is an object that calls  a specific method in a  
> specific context
>  * when its execute method is called. It is essentially a function  
> pointer.
>  * </p>
>  * <p>
>  * Delegates, along with <xref linkend="LzEvent"/>, comprise  
> Laszlo's point to point event system. A delegate represents a named  
> method of an instance. Delegates are mostly registered with events,  
> but they can be used anywhere a function callback would  
> traditionally be called for: for instance, the <xref  
> linkend="LzTimer"/> service takes a delegate as its argument when a  
> timer is started. See the code example on the <xref  
> linkend="LzEvent"/>.
>  * </p>
>  *
>  * @shortdesc The receiver in Laszlo's point-to-point event system.
>  * @see LzEvent
>  */
> class LzDelegate {
>
> /**
>  *
>  * @param Object context: reference to object which will be called
>  * @param String functionName: name of the method to call (a string)
>  * @param Object eventSender: optional; the sender of the event to  
> register the
>  * new delegate for.
>  * @param String eventName: Optional, but required if eventSender is  
> used; The name
>  * of the event to register the new delegate for.
>  */
> function initialize (context, functionName, eventSender, eventName) {
>  super.initialize.apply(this, arguments);
>  // too expensive to leave on all the time
> //     if ($debug) {
> //         this._dbg_created = Debug.backtrace();
> //     }
>  this.c = context;
>  if ( $debug ){
>    if (typeof(functionName) != "string")
>      Debug.warn('LzDelegate functionName must be a string in %w',  
> arguments.caller);
>  }
>  this.f = functionName;
>  if ( eventSender != null ){
>    if ( $debug ) {
>      if (typeof(eventName) != "string") {
>        Debug.warn('LzDelegate eventName must be a string in %w',  
> arguments.caller);
>      }
>    }
>    this.register( eventSender , eventName );
>  }
>
>  this.__delegateID = LzDelegate.__nextID++;
> }
>
> /** @access private */
> static var __nextID = 1;
>
> /** The context in which to call the method
>  * @type Object
>  */
> var c;
>
> /** The name of the method to call
>  * @type String
>  */
> var f;
>
> var lastevent = 0;
> var enabled = true;
> var event_called = false;
>
> /**
>  * Executes the named method in the given context with the given  
> data. Returns
>  * the result of the call. In rare cases, this method may be  
> overriden by a
>  * subclass, and so it is marked 'protected' instead of 'private'.
>  *
>  * @param sd: The data with which to call the method.
>  * @return: The value returned by the method call.
>  * @access protected
>  */
> function execute (sd){
>    // Don't execute if context has been deleted, as that could
>    // 'resurrect' the deleted view, causing a memory leak
>    // Usually this is because a deleted view has idle or timer events
>    // still registered.
>    var context = this.c;
>    if (context) {
>        if (context['__LZdeleted']) {
>            return;
>        }
>        return context[this.f]( sd );
>    }
> }
>
> /**
>  * Registers the delegate for the named event in the given context.  
> <b>NB:</b>
>  * This is the primary way in which events are created. Published  
> events do
>  * not generally exist as objects (with some exceptions) until  
> delegates are
>  * created for them.
>  *
>  * @param Object eventSender: The object which publishes the event.
>  * @param String eventName: The name (string) of the event to  
> register for.
>  */
> function register ( eventSender , eventName){
>    if (! eventSender) {
>        if ($debug) {
>            Debug.error('No eventSender (%w) for %s', eventSender,  
> eventName);
>        }
>        return;
>    }
>
>    var anEvent = eventSender[ eventName ];
>
>    if ( anEvent == LzDeclaredEvent || !anEvent ){
>        // The call used to be unrolled here for swf. I removed it  
> since it
>        // was done during the swf5 days
>        anEvent = new LzEvent( eventSender, eventName , this );
>    } else {
>        anEvent.addDelegate( this );
>    }
>
>    this[ this.lastevent++ ] = anEvent;
>
>    // If debugging, add an informative name
>    if ($debug) {
>        var anEvent = eventSender[ eventName ];
>        if (! anEvent.hasOwnProperty('_dbg_eventSender')) {
>            anEvent._dbg_eventSender = eventSender;
>            anEvent._dbg_eventName = eventName;
>            // too expensive to leave on all the time
> //             anEvent._dbg_created = Debug.backtrace();
>        }
>    }
>    if ($profile) {
>        var anEvent = eventSender[ eventName ];
>        if (! anEvent.hasOwnProperty('_dbg_profileName')) {
>            anEvent._dbg_profileName = eventName;
>        }
>    }
> }
>
> /**
>  *  Unregisters the delegate for all of the events it is registered  
> for.
>  */
> function unregisterAll ( ){
>    for (var i = 0; i< this.lastevent ; i++){
>        this[ i ].removeDelegate( this );
>        this[ i ] = null;
>    }
>    this.lastevent = 0;
> }
>
> /**
>  *  Unregisters the delegate for the given event
>  *  @param LzEvent event: The event to unregister the delegate from.
>  *                       (e.g.  myview.onmouseup)
>  */
> function unregisterFrom ( event ){
>    var keep = [];
>    for (var i = 0; i< this.lastevent ; i++){
>        var ev = this[ i ];
>        if ( ev == event ){
>            ev.removeDelegate( this );
>        } else {
>            keep.push( ev );
>        }
>        this[ i ] = null;
>    }
>    //now fix it
>    this.lastevent = 0;
>    var l = keep.length;
>    for ( var i = 0; i < l; i++ ){
>        this[ this.lastevent++ ] = keep[ i ];
>    }
> }
>
> /**
>  *  Disables the delegate until enable method is called.
>  */
> function disable (){
>    this.enabled = false;
> }
>
> /**
>  *  Enables a delegate that has been disabled
>  */
> function enable (){
>    this.enabled = true;
> }
>
>  /**
>   * Queue for delegates that are deferred during node initialization
>   * @access private
>   */
>  static var __LZdelegatesQueue = [];
>
>  static var __restoreEnv = {};
>  static var __saveEnv = {};
>
>  /**
>   * Drain the delegates queue back to position POS
>   * @access private
>   */
>  static function  __LZdrainDelegatesQueue (pos) {
>    var evq = this.__LZdelegatesQueue;
>    var n = evq.length;
>    var i = pos;
>    if (i < n) {
>      var calledDelegates = new Array;
>      var lockedEvents = new Array;
>      while (i < n) {
>        var e = evq[i];
>        if (e === LzDelegate.__saveEnv) {
>            ++i;
>            continue;
>        } else if (e === LzDelegate.__restoreEnv) {
>            var d;
>            while (d = calledDelegates.pop()) {
>                d.event_called = false;
>            }
>            var e;
>            while (e = lockedEvents.pop()) {
>                e.locked = false;
>            }
>            ++i;
>            continue;
>        }
>        var d = evq[i+1];
>        var sd = evq[i+2];
>        // Mimic sendEvent which prohibits events and delegates from
>        // recursing
>        if (!e.locked) {
>            e.locked = true;
>            lockedEvents.push(e);
>        }
>        if (!d.event_called) {
>          d.event_called = true; //this delegate has been called
>          calledDelegates.push( d );
>          // d.execute( sd ); inlined
>          if (d.c[d.f]) d.c[d.f]( sd );
>        }
>        i+=3;
>      }
>      while (d = calledDelegates.pop()) {
>        d.event_called = false;
>      }
>      while (e = lockedEvents.pop()) {
>        e.locked = false;
>      }
>    }
>    evq.length = pos;
>  }
>
> /** @access private */
> function toString (){
>    return ("Delegate for " + this.c + " calls " + this.f + " " +  
> this.__delegateID );
> }
>
> if ($debug) {
> /**
>  *  If debugging, add an informative name
>  *  @access private
>  */
> function _dbg_name (){
>    var name = Debug.formatToString("%0.48w.%s()", this.c, this.f);
>    if (this[0]) {
>      name += Debug.formatToString("/* handles %w%s */", this[0],  
> this[1]?" ...":"");
>    }
>    return name;
> }
> }
>
> } // End of LzDelegate
>
>
> /**
>  * <p>Events underly most of the functionality in OpenLaszlo   
> applications.
>  * Unlike events in similar systems, OpenLaszlo's events are point- 
> to-point, meaning that there is no general broadcast mechanism for  
> events, and events do not trickle up or down the instance hierarchy.  
> Instead, objects called <xref linkend="LzDelegate"/> <xref  
> linkend="LzDelegate.prototype.register"/> for events, and if they  
> try to register for an event that doesn't exist yet, the system  
> creates the event. </p>
>  *
>  * <p>You can create a delegate explicitly using the <classname  
> link="true">LzDelegate</classname> class, or implicitly by creating  
> an handler.</p>
>  * <p>
>  * Because of the loose type requirements in LZX, calling an event  
> that no delegate is listening for (and which therefore hasn't been  
> created) has no effect. This allows objects to publish many more  
> events than they actually need to create at runtime.
>  * </p>
>  *
>  * <p>There are two syntaxes with which you can specify an event  
> handler: in the tag used to create that object, or by using the
>  * <tagname link="true">handler</tagname> tag.</p>
>  *
>  * <p>To specify an event handler in an object-creation tag, simply  
> include it like any other attribute.  For example,</p>
>  *
>  * <example class="code" extract="false">
>  * &lt;view onmouseover="doSomething()"&gt;
>  *   &lt;method name="doSomething"&gt;
>  *     // code to be executed when mouse is over the view
>  *   &lt;/method&gt;
>  * &lt;/view&gt;
>  * </example>
>  *
>  * <p>If you use the <tagname link="true">handler</tagname> tag, you  
> do not need to include the handler in the tag that creates the  
> object.</p>
>  *
>  * <example class="code" extract="false">
>  * &lt;view&gt;
>  *  &lt;handler name="onmouseover"&gt;
>  *    // code to be executed when the mouse is over the view
>  *  &lt;/name&gt;
>  * &lt;/view&gt;
>  * </example>
>  *
>  * <p> The above two examples are functionally equivalent.  Using  
> the <tagname>handler</tagname> tag, however, can often lead to more  
> readable code because it removes clutter from the object creation  
> tag.</p>
>  *
>  *
>  * <p>Use the <tagname link="true">event</tagname> tag to create the  
> events; then use the <method>sendEvent</method> method to dispatch  
> it. The following example illustrates how to create custom events.</p>
>  *
>  * <example class="program"
>  *          title="A simple example of publishing and listening for  
> a custom event">
>  * &lt;canvas height="40"&gt;
>  *   &lt;simplelayout/&gt;
>  *   &lt;button name="eventSender"
>  *           <em>onmouseover="this.customevent.sendEvent()"
>  *           onmouseout="this.customevent.sendEvent()"/&gt;</em>
>  *
>  *   &lt;event name="customevent"/&gt;
>  *   &lt;view bgcolor="red" width="20" height="20"  
> oninit="this.setupDelegate()"&gt;
>  *     &lt;method name="setupDelegate"&gt;
>  *       this.del = new LzDelegate( this, "respondToEvent" );
>  *       <em>this.del.register( eventSender , "customevent" );</em>
>  *     &lt;/method&gt;
>  *     &lt;method name="respondToEvent"&gt;
>  *
>  *       this.setAttribute('x', this.x + 10);
>  *     &lt;/method&gt;
>  *   &lt;/view&gt;
>  * &lt;/canvas&gt;
>  * </example>
>  *
>  * <p>
>  * Events can be sent with a single argument, which usually conveys  
> information about the property that changed. The default behavior of  
> the <xref linkend="LzNode.prototype.setAttribute"/> method is to set  
> the named property and send the event called "on" + property. This  
> is general mechanism that updates constraints in a OpenLaszlo  
> programs. For instance, when a view changes its <attribute>x</ 
> attribute> position, it sends the event <event>onx</event> with the  
> new value for its <attribute>x</attribute> property.
>  *
>  * </p>
>  *
>  * <example class="program"
>  *          title="Event sending in response to setting an attribute">
>  * &lt;canvas height="40"&gt;
>  *   &lt;simplelayout/&gt;
>  *   &lt;button name="eventSender"
>  *           <em>onmouseover="this.setAttribute('avalue',  
> this.avalue + 10)"
>  *           onmouseout="this.setAttribute('avalue', this.avalue +  
> 5)"</em>&gt;
>  *     &lt;attribute name="avalue" value="0"/&gt;
>  *   &lt;/button&gt;
>  *
>  *   &lt;view bgcolor="red" width="20" height="20"  
> oninit="this.setupDelegate()"&gt;
>  *     &lt;method name="setupDelegate"&gt;
>  *       this.del = new LzDelegate(this, "respondToEvent");
>  *       this.del.register(eventSender, <em>"onavalue"</em>);
>  *     &lt;/method&gt;
>  *     &lt;method name="respondToEvent" args="v"&gt;
>  *       this.setAttribute('x' , v);
>  *     &lt;/method&gt;
>  *
>  *   &lt;/view&gt;
>  * &lt;/canvas&gt;
>  * </example>
>  *
>  * @shortdesc The sender in Laszlo's point-to-point event system.
>  * @see LzDelegate
>  */
>
> class LzEvent {
>
> /**
>  *  @param Object eventSender: The owner of this event
>  *  @param String eventName: The name of this event.
>  */
> function initialize ( eventSender , eventName , d ){
>    super.initialize.apply(this, arguments);
>
>    var _evs = eventSender._events;
>    if (_evs == null ){
>        eventSender._events = [ this ];
>    } else {
>        _evs.push ( this );
>    }
>    eventSender[ eventName ] = this;
>    if ( d ){
>        this.delegateList = [d];
>        this.ready = true;
>    }else{
>        this.delegateList = [];
>    }
>
>    // If debugging, add an informative name
>    if ($debug) {
>      this._dbg_eventSender = eventSender;
>      this._dbg_eventName = eventName;
>    }
>    if ($profile) {
>      this._dbg_profileName = eventName;
>    }
> }
>
> // Because this is not created with Class
> //prototype.classname = "LzEvent";
>
> /** True when event is being sent.
>  * @type Boolean
>  */
> var locked = false;
>
> /** True when event is ready to be sent.
>  *
>  */
> var ready = false;
>
> /**
>  *  Adds the given delegate to the event's delegate list. Although  
> this listed
>  *  as a public function it should rarely be called explicitly -- it  
> is used
>  *  exclusively by <b><i>LzDelegate</i>.register</b>
>  *
>  *  @param LzDelegate d: The delegate to add to the list of  
> delegates called by the event.
>  */
> function addDelegate (d){
>    this.ready = true;
>    this.delegateList.push(d);
> }
>
>
>
> /**
>  *  Sends the event, passing its argument as the data to all the  
> called
>  *  delegates
>  *
>  *  @param sd: The data to send with the event.
>  */
> function sendEvent ( sd ){
>    if ( this.locked ) { return; } //don't allow for multiple calls
>
>    var dll = this.delegateList.length;
>    if (dll == 0) { return; }
>
>    if ($profile) {
>        var nm = this._dbg_profileName;
>
>        if (nm) {
>            Profiler.event(nm, 'calls');
>        }
>    }
>
>    this.locked = true;
>
>    var calledDelegates = new Array;
>
>    var d;
>    var evq = LzDelegate.__LZdelegatesQueue;
>    var evqFlag = false;
>    for (var i = dll; i >= 0; i--){
>        d = this.delegateList[ i ];
>        //pointer may be bad due to deletions
>        if ( d && ! d.event_called){
>            d.event_called = true; //this delegate has been called
>            calledDelegates.push( d );
>            // We don't worry about deleted contexts here, because
>            // we assume that delegates registered on events are
>            // properly managed
>            if (d.enabled && d.c) {
>                if (d.c.__LZdeferDelegates) {
>                  if (!evqFlag) {
>                    evqFlag = true;
>                    evq.push(LzDelegate.__saveEnv);
>                  }
>                  evq.push(this, d, sd);
>                } else {
>                  // d.execute( sd ); inlined
>                  if (d.c[d.f]) d.c[d.f]( sd );
>                }
>            }
>        }
>    }
>
>    if (evqFlag) {
>        evq.push(LzDelegate.__restoreEnv);
>    }
>
>    while (d = calledDelegates.pop() ){
>        d.event_called = false;
>    }
>
>    if ($profile) {
>        var nm = this._dbg_profileName;
>        if (nm) {
>            Profiler.event(nm, 'returns');
>        }
>    }
>
>    this.locked = false;
> }
>
> /**
>  *  Removes the delegate from the delegate list. In practice, this  
> is rarely
>  *  called explicitly, since it does not update the delegate's list  
> of stored
>  *  events. Right now, this is called only by <b><i>LzDelegate</i>.
>  *  unregisterAll</b> Delegates should support a simple unregister  
> command, that
>  *  unregisters them for a single event, but to date, that has not  
> proven
>  *  necessary
>  *
>  *  @param LzDelegate d: The delegate to remove from the delegateList.
> */
> function removeDelegate ( d ){
>    var dll = this.delegateList.length;
>    for (var i = 0; i < dll; i++){
>        if (this.delegateList[i] == d){
>            this.delegateList.splice(i, 1);
>            break;
>        }
>    }
>    if ( this.delegateList.length == 0 ){
>        this.ready = false;
>    }
> }
>
> /**
>  *  Removes all delegates from call list
>  */
> function clearDelegates (){
>    while (this.delegateList.length ){
>        this.delegateList[ 0 ].unregisterFrom( this );
>    }
>    //this.delegateList = [];
>    this.ready = false;
> }
>
> /**
>  *  Returns the number of delegates registered for the event
>  *  @return Number: The number of delegates registered for the event.
>  */
> function getDelegateCount ( ){
>    return this.delegateList.length;
> }
>
> function toString (){
>    return ( "LzEvent");
> }
>
> if ($debug) {
> /**
>  *  If debugging, add an informative name
>  *  @access private
>  */
> function _dbg_name (){
>    return Debug.formatToString("%0.48w.%s", this._dbg_eventSender,  
> this._dbg_eventName);
> }
> }
>
> } // End of LzEvent




More information about the Laszlo-dev mailing list