source: branches/comu/html/test/kakinaka/js/event/event-debug.js @ 2

Revision 2, 46.0 KB checked in by root, 17 years ago (diff)

new import

Line 
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5Version: 0.11.4
6*/
7
8
9/**
10 * The CustomEvent class lets you define events for your application
11 * that can be subscribed to by one or more independent component.
12 *
13 * @param {String}  type The type of event, which is passed to the callback
14 *                  when the event fires
15 * @param {Object}  oScope The context the event will fire from.  "this" will
16 *                  refer to this object in the callback.  Default value:
17 *                  the window object.  The listener can override this.
18 * @param {boolean} silent pass true to prevent the event from writing to
19 *                  the log system
20 * @namespace YAHOO.util
21 * @class CustomEvent
22 * @constructor
23 */
24YAHOO.util.CustomEvent = function(type, oScope, silent) {
25
26    /**
27     * The type of event, returned to subscribers when the event fires
28     * @property type
29     * @type string
30     */
31    this.type = type;
32
33    /**
34     * The scope the the event will fire from by default.  Defaults to the window
35     * obj
36     * @property scope
37     * @type object
38     */
39    this.scope = oScope || window;
40
41    /**
42     * By default all custom events are logged in the debug build, set silent
43     * to true to disable logging for this event.
44     * @property silent
45     * @type boolean
46     */
47    this.silent = silent;
48
49    /**
50     * The subscribers to this event
51     * @property subscribers
52     * @type Subscriber[]
53     */
54    this.subscribers = [];
55
56    if (!this.silent) {
57        YAHOO.log( "Creating " + this, "info", "Event" );
58    }
59
60    // Only add subscribe events for events that are not generated by CustomEvent
61    //if (oScope && (oScope.constructor != this.constructor)) {
62
63        /*
64         * Custom events provide a custom event that fires whenever there is
65         * a new subscriber to the event.  This provides an opportunity to
66         * handle the case where there is a non-repeating event that has
67         * already fired has a new subscriber.
68         *
69         * type CustomEvent
70         */
71        //this.subscribeEvent =
72                //new YAHOO.util.CustomEvent("subscribe", this, true);
73
74    //}
75};
76
77YAHOO.util.CustomEvent.prototype = {
78    /**
79     * Subscribes the caller to this event
80     * @method subscribe
81     * @param {Function} fn       The function to execute
82     * @param {Object}   obj      An object to be passed along when the event fires
83     * @param {boolean}  bOverride If true, the obj passed in becomes the execution
84     *                            scope of the listener
85     */
86    subscribe: function(fn, obj, bOverride) {
87        //if (this.subscribeEvent) {
88            //this.subscribeEvent.fire(fn, obj, bOverride);
89        //}
90
91        this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, bOverride) );
92    },
93
94    /**
95     * Unsubscribes the caller from this event
96     * @method unsubscribe
97     * @param {Function} fn  The function to execute
98     * @param {Object}   obj An object to be passed along when the event fires
99     * @return {boolean} True if the subscriber was found and detached.
100     */
101    unsubscribe: function(fn, obj) {
102        var found = false;
103        for (var i=0, len=this.subscribers.length; i<len; ++i) {
104            var s = this.subscribers[i];
105            if (s && s.contains(fn, obj)) {
106                this._delete(i);
107                found = true;
108            }
109        }
110
111        return found;
112    },
113
114    /**
115     * Notifies the subscribers.  The callback functions will be executed
116     * from the scope specified when the event was created, and with the following
117     * parameters:
118     *   <pre>
119     *   - The type of event
120     *   - All of the arguments fire() was executed with as an array
121     *   - The custom object (if any) that was passed into the subscribe() method
122     *   </pre>
123     * @method fire
124     * @param {Array} an arbitrary set of parameters to pass to the handler
125     */
126    fire: function() {
127        var len=this.subscribers.length;
128        if (!len && this.silent) {
129            return;
130        }
131
132        var args = [];
133
134        for (var i=0; i<arguments.length; ++i) {
135            args.push(arguments[i]);
136        }
137
138        if (!this.silent) {
139            YAHOO.log( "Firing "       + this  + ", " +
140                       "args: "        + args  + ", " +
141                       "subscribers: " + len,
142                       "info", "Event"                  );
143        }
144
145        for (i=0; i<len; ++i) {
146            var s = this.subscribers[i];
147            if (s) {
148                if (!this.silent) {
149                    YAHOO.log( this.type + "->" + (i+1) + ": " +  s, "info", "Event" );
150                }
151                var scope = (s.override) ? s.obj : this.scope;
152                s.fn.call(scope, this.type, args, s.obj);
153            }
154        }
155    },
156
157    /**
158     * Removes all listeners
159     * @method unsubscribeAll
160     */
161    unsubscribeAll: function() {
162        for (var i=0, len=this.subscribers.length; i<len; ++i) {
163            this._delete(len - 1 - i);
164        }
165    },
166
167    /**
168     * @method _delete
169     * @private
170     */
171    _delete: function(index) {
172        var s = this.subscribers[index];
173        if (s) {
174            delete s.fn;
175            delete s.obj;
176        }
177
178        // delete this.subscribers[index];
179        this.subscribers.splice(index, 1);
180    },
181
182    /**
183     * @method toString
184     */
185    toString: function() {
186         return "CustomEvent: " + "'" + this.type  + "', " +
187             "scope: " + this.scope;
188
189    }
190};
191
192/////////////////////////////////////////////////////////////////////
193
194/**
195 * Stores the subscriber information to be used when the event fires.
196 * @param {Function} fn       The function to execute
197 * @param {Object}   obj      An object to be passed along when the event fires
198 * @param {boolean}  bOverride If true, the obj passed in becomes the execution
199 *                            scope of the listener
200 * @class Subscriber
201 * @constructor
202 */
203YAHOO.util.Subscriber = function(fn, obj, bOverride) {
204
205    /**
206     * The callback that will be execute when the event fires
207     * @property fn
208     * @type function
209     */
210    this.fn = fn;
211
212    /**
213     * An optional custom object that will passed to the callback when
214     * the event fires
215     * @property obj
216     * @type object
217     */
218    this.obj = obj || null;
219
220    /**
221     * The default execution scope for the event listener is defined when the
222     * event is created (usually the object which contains the event).
223     * By setting override to true, the execution scope becomes the custom
224     * object passed in by the subscriber
225     * @property override
226     * @type boolean
227     */
228    this.override = (bOverride);
229};
230
231/**
232 * Returns true if the fn and obj match this objects properties.
233 * Used by the unsubscribe method to match the right subscriber.
234 *
235 * @method contains
236 * @param {Function} fn the function to execute
237 * @param {Object} obj an object to be passed along when the event fires
238 * @return {boolean} true if the supplied arguments match this
239 *                   subscriber's signature.
240 */
241YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
242    return (this.fn == fn && this.obj == obj);
243};
244
245/**
246 * @method toString
247 */
248YAHOO.util.Subscriber.prototype.toString = function() {
249    return "Subscriber { obj: " + (this.obj || "")  +
250           ", override: " +  (this.override || "no") + " }";
251};
252
253// The first instance of Event will win if it is loaded more than once.
254if (!YAHOO.util.Event) {
255
256/**
257 * The event utility provides functions to add and remove event listeners,
258 * event cleansing.  It also tries to automatically remove listeners it
259 * registers during the unload event.
260 * @namespace YAHOO.util
261 * @class Event
262 */
263    YAHOO.util.Event = function() {
264
265        /**
266         * True after the onload event has fired
267         * @property loadComplete
268         * @type boolean
269         * @private
270         */
271        var loadComplete =  false;
272
273        /**
274         * Cache of wrapped listeners
275         * @property listeners
276         * @type array
277         * @private
278         */
279        var listeners = [];
280
281        /**
282         * Listeners that will be attached during the onload event
283         * @property delayedListeners
284         * @type array
285         * @private
286         */
287        var delayedListeners = [];
288
289        /**
290         * User-defined unload function that will be fired before all events
291         * are detached
292         * @property unloadListeners
293         * @type array
294         * @private
295         */
296        var unloadListeners = [];
297
298        /**
299         * Cache of DOM0 event handlers to work around issues with DOM2 events
300         * in Safari
301         * @property legacyEvents
302         * @private
303         */
304        var legacyEvents = [];
305
306        /**
307         * Listener stack for DOM0 events
308         * @property legacyHandlers
309         * @private
310         */
311        var legacyHandlers = [];
312
313        /**
314         * The number of times to poll after window.onload.  This number is
315         * increased if additional late-bound handlers are requested after
316         * the page load.
317         * @property retryCount
318         * @private
319         */
320        var retryCount = 0;
321
322        /**
323         * onAvailable listeners
324         * @property onAvailStack
325         * @private
326         */
327        var onAvailStack = [];
328
329        /**
330         * Lookup table for legacy events
331         * @property legacyMap
332         * @private
333         */
334        var legacyMap = [];
335
336        /**
337         * Counter for auto id generation
338         * @property counter
339         * @private
340         */
341        var counter = 0;
342
343        return { // PREPROCESS
344
345            /**
346             * The number of times we should look for elements that are not
347             * in the DOM at the time the event is requested after the document
348             * has been loaded.  The default is 200@amp;50 ms, so it will poll
349             * for 10 seconds or until all outstanding handlers are bound
350             * (whichever comes first).
351             * @property POLL_RETRYS
352             * @type int
353             */
354            POLL_RETRYS: 200,
355
356            /**
357             * The poll interval in milliseconds
358             * @property POLL_INTERVAL
359             * @type int
360             */
361            POLL_INTERVAL: 50,
362
363            /**
364             * Element to bind, int constant
365             * @property EL
366             * @type int
367             */
368            EL: 0,
369
370            /**
371             * Type of event, int constant
372             * @property TYPE
373             * @type int
374             */
375            TYPE: 1,
376
377            /**
378             * Function to execute, int constant
379             * @property FN
380             * @type int
381             */
382            FN: 2,
383
384            /**
385             * Function wrapped for scope correction and cleanup, int constant
386             * @property WFN
387             * @type int
388             */
389            WFN: 3,
390
391            /**
392             * Object passed in by the user that will be returned as a
393             * parameter to the callback, int constant
394             * @property SCOPE
395             * @type int
396             */
397            SCOPE: 3,
398
399            /**
400             * Adjusted scope, either the element we are registering the event
401             * on or the custom object passed in by the listener, int constant
402             * @property ADJ_SCOPE
403             * @type int
404             */
405            ADJ_SCOPE: 4,
406
407            /**
408             * Safari detection is necessary to work around the preventDefault
409             * bug that makes it so you can't cancel a href click from the
410             * handler.  There is not a capabilities check we can use here.
411             * @property isSafari
412             * @private
413             */
414            isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),
415
416            /**
417             * IE detection needed to properly calculate pageX and pageY.
418             * capabilities checking didn't seem to work because another
419             * browser that does not provide the properties have the values
420             * calculated in a different manner than IE.
421             * @property isIE
422             * @private
423             */
424            isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) &&
425                    navigator.userAgent.match(/msie/gi)),
426
427            /**
428             * @method addDelayedListener
429             * @private
430             */
431            addDelayedListener: function(el, sType, fn, oScope, bOverride) {
432                delayedListeners[delayedListeners.length] =
433                    [el, sType, fn, oScope, bOverride];
434
435                // If this happens after the inital page load, we need to
436                // reset the poll counter so that we continue to search for
437                // the element for a fixed period of time.
438                if (loadComplete) {
439                    retryCount = this.POLL_RETRYS;
440                    this.startTimeout(0);
441                    // this._tryPreloadAttach();
442                }
443            },
444
445            /**
446             * @method startTimeout
447             * @private
448             */
449            startTimeout: function(interval) {
450                var i = (interval || interval === 0) ? interval : this.POLL_INTERVAL;
451                var self = this;
452                var callback = function() { self._tryPreloadAttach(); };
453                this.timeout = setTimeout(callback, i);
454            },
455
456            /**
457             * Executes the supplied callback when the item with the supplied
458             * id is found.  This is meant to be used to execute behavior as
459             * soon as possible as the page loads.  If you use this after the
460             * initial page load it will poll for a fixed time for the element.
461             * The number of times it will poll and the frequency are
462             * configurable.  By default it will poll for 10 seconds.
463             *
464             * @method onAvailable
465             *
466             * @param {string}   p_id the id of the element to look for.
467             * @param {function} p_fn what to execute when the element is found.
468             * @param {object}   p_obj an optional object to be passed back as
469             *                   a parameter to p_fn.
470             * @param {boolean}  p_override If set to true, p_fn will execute
471             *                   in the scope of p_obj
472             *
473             */
474            onAvailable: function(p_id, p_fn, p_obj, p_override) {
475                onAvailStack.push( { id:       p_id,
476                                     fn:       p_fn,
477                                     obj:      p_obj,
478                                     override: p_override } );
479
480                retryCount = this.POLL_RETRYS;
481                this.startTimeout(0);
482                // this._tryPreloadAttach();
483            },
484
485            /**
486             * Appends an event handler
487             *
488             * @method addListener
489             *
490             * @param {Object}   el        The html element to assign the
491             *                             event to
492             * @param {String}   sType     The type of event to append
493             * @param {Function} fn        The method the event invokes
494             * @param {Object}   oScope    An arbitrary object that will be
495             *                             passed as a parameter to the handler
496             * @param {boolean}  bOverride If true, the obj passed in becomes
497             *                             the execution scope of the listener
498             * @return {boolean} True if the action was successful or defered,
499             *                        false if one or more of the elements
500             *                        could not have the event bound to it.
501             */
502            addListener: function(el, sType, fn, oScope, bOverride) {
503
504                if (!fn || !fn.call) {
505                    // this.logger.debug("Error, function is not valid " + fn);
506                    return false;
507                }
508
509                // The el argument can be an array of elements or element ids.
510                if ( this._isValidCollection(el)) {
511                    var ok = true;
512                    for (var i=0,len=el.length; i<len; ++i) {
513                        ok = ( this.on(el[i],
514                                       sType,
515                                       fn,
516                                       oScope,
517                                       bOverride) && ok );
518                    }
519                    return ok;
520
521                } else if (typeof el == "string") {
522                    var oEl = this.getEl(el);
523                    // If the el argument is a string, we assume it is
524                    // actually the id of the element.  If the page is loaded
525                    // we convert el to the actual element, otherwise we
526                    // defer attaching the event until onload event fires
527
528                    // check to see if we need to delay hooking up the event
529                    // until after the page loads.
530                    if (loadComplete && oEl) {
531                        el = oEl;
532                    } else {
533                        // defer adding the event until onload fires
534                        this.addDelayedListener(el,
535                                                sType,
536                                                fn,
537                                                oScope,
538                                                bOverride);
539
540                        return true;
541                    }
542                }
543
544                // Element should be an html element or an array if we get
545                // here.
546                if (!el) {
547                    // this.logger.debug("unable to attach event " + sType);
548                    return false;
549                }
550
551                // we need to make sure we fire registered unload events
552                // prior to automatically unhooking them.  So we hang on to
553                // these instead of attaching them to the window and fire the
554                // handles explicitly during our one unload event.
555                if ("unload" == sType && oScope !== this) {
556                    unloadListeners[unloadListeners.length] =
557                            [el, sType, fn, oScope, bOverride];
558                    return true;
559                }
560
561                // this.logger.debug("Adding handler: " + el + ", " + sType);
562
563                // if the user chooses to override the scope, we use the custom
564                // object passed in, otherwise the executing scope will be the
565                // HTML element that the event is registered on
566                var scope = (bOverride) ? oScope : el;
567
568                // wrap the function so we can return the oScope object when
569                // the event fires;
570                var wrappedFn = function(e) {
571                        return fn.call(scope, YAHOO.util.Event.getEvent(e),
572                                oScope);
573                    };
574
575                var li = [el, sType, fn, wrappedFn, scope];
576                var index = listeners.length;
577                // cache the listener so we can try to automatically unload
578                listeners[index] = li;
579
580                if (this.useLegacyEvent(el, sType)) {
581                    var legacyIndex = this.getLegacyIndex(el, sType);
582
583                    // Add a new dom0 wrapper if one is not detected for this
584                    // element
585                    if ( legacyIndex == -1 ||
586                                el != legacyEvents[legacyIndex][0] ) {
587
588                        legacyIndex = legacyEvents.length;
589                        legacyMap[el.id + sType] = legacyIndex;
590
591                        // cache the signature for the DOM0 event, and
592                        // include the existing handler for the event, if any
593                        legacyEvents[legacyIndex] =
594                            [el, sType, el["on" + sType]];
595                        legacyHandlers[legacyIndex] = [];
596
597                        el["on" + sType] =
598                            function(e) {
599                                YAHOO.util.Event.fireLegacyEvent(
600                                    YAHOO.util.Event.getEvent(e), legacyIndex);
601                            };
602                    }
603
604                    // add a reference to the wrapped listener to our custom
605                    // stack of events
606                    //legacyHandlers[legacyIndex].push(index);
607                    legacyHandlers[legacyIndex].push(li);
608
609                // DOM2 Event model
610                } else if (el.addEventListener) {
611                    // this.logger.debug("adding DOM event: " + el.id +
612                    // ", " + sType);
613                    el.addEventListener(sType, wrappedFn, false);
614                // IE
615                } else if (el.attachEvent) {
616                    el.attachEvent("on" + sType, wrappedFn);
617                }
618
619                return true;
620
621            },
622
623            /**
624             * When using legacy events, the handler is routed to this object
625             * so we can fire our custom listener stack.
626             * @method fireLegacyEvent
627             * @private
628             */
629            fireLegacyEvent: function(e, legacyIndex) {
630                // this.logger.debug("fireLegacyEvent " + legacyIndex);
631                var ok = true;
632
633                var le = legacyHandlers[legacyIndex];
634                for (var i=0,len=le.length; i<len; ++i) {
635                    var li = le[i];
636                    if ( li && li[this.WFN] ) {
637                        var scope = li[this.ADJ_SCOPE];
638                        var ret = li[this.WFN].call(scope, e);
639                        ok = (ok && ret);
640                    }
641                }
642
643                return ok;
644            },
645
646            /**
647             * Returns the legacy event index that matches the supplied
648             * signature
649             * @method getLegacyIndex
650             * @private
651             */
652            getLegacyIndex: function(el, sType) {
653                var key = this.generateId(el) + sType;
654                if (typeof legacyMap[key] == "undefined") {
655                    return -1;
656                } else {
657                    return legacyMap[key];
658                }
659            },
660
661            /**
662             * Logic that determines when we should automatically use legacy
663             * events instead of DOM2 events.
664             * @method useLegacyEvent
665             * @private
666             */
667            useLegacyEvent: function(el, sType) {
668                if (!el.addEventListener && !el.attachEvent) {
669                    return true;
670                } else if (this.isSafari) {
671                    if ("click" == sType || "dblclick" == sType) {
672                        return true;
673                    }
674                }
675                return false;
676            },
677
678            /**
679             * Removes an event handler
680             *
681             * @method removeListener
682             *
683             * @param {Object} el the html element or the id of the element to
684             * assign the event to.
685             * @param {String} sType the type of event to remove
686             * @param {Function} fn the method the event invokes
687             * @return {boolean} true if the unbind was successful, false
688             * otherwise
689             */
690            removeListener: function(el, sType, fn, index) {
691
692                if (!fn || !fn.call) {
693                    // this.logger.debug("Error, function is not valid " + fn);
694                    return false;
695                }
696
697                var i, len;
698
699                // The el argument can be a string
700                if (typeof el == "string") {
701                    el = this.getEl(el);
702                // The el argument can be an array of elements or element ids.
703                } else if ( this._isValidCollection(el)) {
704                    var ok = true;
705                    for (i=0,len=el.length; i<len; ++i) {
706                        ok = ( this.removeListener(el[i], sType, fn) && ok );
707                    }
708                    return ok;
709                }
710
711
712                if ("unload" == sType) {
713
714                    for (i=0, len=unloadListeners.length; i<len; i++) {
715                        var li = unloadListeners[i];
716                        if (li &&
717                            li[0] == el &&
718                            li[1] == sType &&
719                            li[2] == fn) {
720                                unloadListeners.splice(i, 1);
721                                return true;
722                        }
723                    }
724
725                    return false;
726                }
727
728                var cacheItem = null;
729
730                //var index = arguments[3];
731
732                if ("undefined" == typeof index) {
733                    index = this._getCacheIndex(el, sType, fn);
734                }
735
736                if (index >= 0) {
737                    cacheItem = listeners[index];
738                }
739
740                if (!el || !cacheItem) {
741                    // this.logger.debug("cached listener not found");
742                    return false;
743                }
744
745                // this.logger.debug("Removing handler: " + el + ", " + sType);
746
747                if (this.useLegacyEvent(el, sType)) {
748                    var legacyIndex = this.getLegacyIndex(el, sType);
749                    var llist = legacyHandlers[legacyIndex];
750                    if (llist) {
751                        for (i=0, len=llist.length; i<len; ++i) {
752                            li = llist[i];
753                            if (li &&
754                                li[this.EL] == el &&
755                                li[this.TYPE] == sType &&
756                                li[this.FN] == fn) {
757                                    llist.splice(i, 1);
758                            }
759                        }
760                    }
761
762                } else if (el.removeEventListener) {
763                    el.removeEventListener(sType, cacheItem[this.WFN], false);
764                    // this.logger.debug("adsf");
765                } else if (el.detachEvent) {
766                    el.detachEvent("on" + sType, cacheItem[this.WFN]);
767                }
768
769                // removed the wrapped handler
770                delete listeners[index][this.WFN];
771                delete listeners[index][this.FN];
772                listeners.splice(index, 1);
773
774                return true;
775
776            },
777
778            /**
779             * Returns the event's target element
780             * @method getTarget
781             * @param {Event} ev the event
782             * @param {boolean} resolveTextNode when set to true the target's
783             *                  parent will be returned if the target is a
784             *                  text node.  @deprecated, the text node is
785             *                  now resolved automatically
786             * @return {HTMLElement} the event's target
787             */
788            getTarget: function(ev, resolveTextNode) {
789                var t = ev.target || ev.srcElement;
790                return this.resolveTextNode(t);
791            },
792
793            /**
794             * In some cases, some browsers will return a text node inside
795             * the actual element that was targeted.  This normalizes the
796             * return value for getTarget and getRelatedTarget.
797             * @method resolveTextNode
798             * @param {HTMLElement} node to resolve
799             * @return  the normized node
800             */
801            resolveTextNode: function(node) {
802                if (node && node.nodeName &&
803                        "#TEXT" == node.nodeName.toUpperCase()) {
804                    return node.parentNode;
805                } else {
806                    return node;
807                }
808            },
809
810            /**
811             * Returns the event's pageX
812             * @method getPageX
813             * @param {Event} ev the event
814             * @return {int} the event's pageX
815             */
816            getPageX: function(ev) {
817                var x = ev.pageX;
818                if (!x && 0 !== x) {
819                    x = ev.clientX || 0;
820
821                    if ( this.isIE ) {
822                        x += this._getScrollLeft();
823                    }
824                }
825
826                return x;
827            },
828
829            /**
830             * Returns the event's pageY
831             * @method getPageY
832             * @param {Event} ev the event
833             * @return {int} the event's pageY
834             */
835            getPageY: function(ev) {
836                var y = ev.pageY;
837                if (!y && 0 !== y) {
838                    y = ev.clientY || 0;
839
840                    if ( this.isIE ) {
841                        y += this._getScrollTop();
842                    }
843                }
844
845
846                return y;
847            },
848
849            /**
850             * Returns the pageX and pageY properties as an indexed array.
851             * @method getXY
852             * @type int[]
853             */
854            getXY: function(ev) {
855                return [this.getPageX(ev), this.getPageY(ev)];
856            },
857
858            /**
859             * Returns the event's related target
860             * @method getRelatedTarget
861             * @param {Event} ev the event
862             * @return {HTMLElement} the event's relatedTarget
863             */
864            getRelatedTarget: function(ev) {
865                var t = ev.relatedTarget;
866                if (!t) {
867                    if (ev.type == "mouseout") {
868                        t = ev.toElement;
869                    } else if (ev.type == "mouseover") {
870                        t = ev.fromElement;
871                    }
872                }
873
874                return this.resolveTextNode(t);
875            },
876
877            /**
878             * Returns the time of the event.  If the time is not included, the
879             * event is modified using the current time.
880             * @method getTime
881             * @param {Event} ev the event
882             * @return {Date} the time of the event
883             */
884            getTime: function(ev) {
885                if (!ev.time) {
886                    var t = new Date().getTime();
887                    try {
888                        ev.time = t;
889                    } catch(e) {
890                        // can't set the time property
891                        return t;
892                    }
893                }
894
895                return ev.time;
896            },
897
898            /**
899             * Convenience method for stopPropagation + preventDefault
900             * @method stopEvent
901             * @param {Event} ev the event
902             */
903            stopEvent: function(ev) {
904                this.stopPropagation(ev);
905                this.preventDefault(ev);
906            },
907
908            /**
909             * Stops event propagation
910             * @method stopPropagation
911             * @param {Event} ev the event
912             */
913            stopPropagation: function(ev) {
914                if (ev.stopPropagation) {
915                    ev.stopPropagation();
916                } else {
917                    ev.cancelBubble = true;
918                }
919            },
920
921            /**
922             * Prevents the default behavior of the event
923             * @method preventDefault
924             * @param {Event} ev the event
925             */
926            preventDefault: function(ev) {
927                if (ev.preventDefault) {
928                    ev.preventDefault();
929                } else {
930                    ev.returnValue = false;
931                }
932            },
933
934            /**
935             * Finds the event in the window object, the caller's arguments, or
936             * in the arguments of another method in the callstack.  This is
937             * executed automatically for events registered through the event
938             * manager, so the implementer should not normally need to execute
939             * this function at all.
940             * @method getEvent
941             * @param {Event} the event parameter from the handler
942             * @return {Event} the event
943             */
944            getEvent: function(e) {
945                var ev = e || window.event;
946
947                if (!ev) {
948                    var c = this.getEvent.caller;
949                    while (c) {
950                        ev = c.arguments[0];
951                        if (ev && Event == ev.constructor) {
952                            break;
953                        }
954                        c = c.caller;
955                    }
956                }
957
958                return ev;
959            },
960
961            /**
962             * Returns the charcode for an event
963             * @method getCharCode
964             * @param {Event} ev the event
965             * @return {int} the event's charCode
966             */
967            getCharCode: function(ev) {
968                return ev.charCode || ((ev.type == "keypress") ? ev.keyCode : 0);
969            },
970
971            /**
972             * Locating the saved event handler data by function ref
973             *
974             * @method _getCacheIndex
975             * @private
976             */
977            _getCacheIndex: function(el, sType, fn) {
978                for (var i=0,len=listeners.length; i<len; ++i) {
979                    var li = listeners[i];
980                    if ( li                 &&
981                         li[this.FN] == fn  &&
982                         li[this.EL] == el  &&
983                         li[this.TYPE] == sType ) {
984                        return i;
985                    }
986                }
987
988                return -1;
989            },
990
991            /**
992             * Generates an unique ID for the element if it does not already
993             * have one.
994             * @method generateId
995             * @param el the element
996             * @return {string} the id of the element
997             */
998            generateId: function(el) {
999                var id = el.id;
1000
1001                if (!id) {
1002                    id = "yuievtautoid-" + counter;
1003                    ++counter;
1004                    el.id = id;
1005                }
1006
1007                return id;
1008            },
1009
1010
1011            /**
1012             * We want to be able to use getElementsByTagName as a collection
1013             * to attach a group of events to.  Unfortunately, different
1014             * browsers return different types of collections.  This function
1015             * tests to determine if the object is array-like.  It will also
1016             * fail if the object is an array, but is empty.
1017             * @method _isValidCollection
1018             * @param o the object to test
1019             * @return {boolean} true if the object is array-like and populated
1020             * @private
1021             */
1022            _isValidCollection: function(o) {
1023                // this.logger.debug(o.constructor.toString())
1024                // this.logger.debug(typeof o)
1025
1026                return ( o                    && // o is something
1027                         o.length             && // o is indexed
1028                         typeof o != "string" && // o is not a string
1029                         !o.tagName           && // o is not an HTML element
1030                         !o.alert             && // o is not a window
1031                         typeof o[0] != "undefined" );
1032
1033            },
1034
1035            /**
1036             * @private
1037             * @property elCache
1038             * DOM element cache
1039             */
1040            elCache: {},
1041
1042            /**
1043             * We cache elements bound by id because when the unload event
1044             * fires, we can no longer use document.getElementById
1045             * @method getEl
1046             * @private
1047             */
1048            getEl: function(id) {
1049                return document.getElementById(id);
1050            },
1051
1052            /**
1053             * Clears the element cache
1054             * @deprecated
1055             * @private
1056             */
1057            clearCache: function() { },
1058
1059            /**
1060             * hook up any deferred listeners
1061             * @method _load
1062             * @private
1063             */
1064            _load: function(e) {
1065                loadComplete = true;
1066                var EU = YAHOO.util.Event;
1067                EU._simpleRemove(window, "load", EU._load);
1068            },
1069
1070            /**
1071             * Polling function that runs before the onload event fires,
1072             * attempting to attach to DOM Nodes as soon as they are
1073             * available
1074             * @method _tryPreloadAttach
1075             * @private
1076             */
1077            _tryPreloadAttach: function() {
1078
1079                if (this.locked) {
1080                    return false;
1081                }
1082
1083                this.locked = true;
1084
1085                // this.logger.debug("tryPreloadAttach");
1086
1087                // keep trying until after the page is loaded.  We need to
1088                // check the page load state prior to trying to bind the
1089                // elements so that we can be certain all elements have been
1090                // tested appropriately
1091                var tryAgain = !loadComplete;
1092                if (!tryAgain) {
1093                    tryAgain = (retryCount > 0);
1094                }
1095
1096                // Delayed listeners
1097                var stillDelayed = [];
1098
1099                for (var i=0,len=delayedListeners.length; i<len; ++i) {
1100                    var d = delayedListeners[i];
1101                    // There may be a race condition here, so we need to
1102                    // verify the array element is usable.
1103                    if (d) {
1104
1105                        // el will be null if document.getElementById did not
1106                        // work
1107                        var el = this.getEl(d[this.EL]);
1108
1109                        if (el) {
1110                            // this.logger.debug("attaching: " + d[this.EL]);
1111                            this.on(el, d[this.TYPE], d[this.FN],
1112                                    d[this.SCOPE], d[this.ADJ_SCOPE]);
1113                            delete delayedListeners[i];
1114                        } else {
1115                            stillDelayed.push(d);
1116                        }
1117                    }
1118                }
1119
1120                delayedListeners = stillDelayed;
1121
1122                // onAvailable
1123                var notAvail = [];
1124                for (i=0,len=onAvailStack.length; i<len ; ++i) {
1125                    var item = onAvailStack[i];
1126                    if (item) {
1127                        el = this.getEl(item.id);
1128
1129                        if (el) {
1130                            var scope = (item.override) ? item.obj : el;
1131                            item.fn.call(scope, item.obj);
1132                            delete onAvailStack[i];
1133                        } else {
1134                            notAvail.push(item);
1135                        }
1136                    }
1137                }
1138
1139                retryCount = (stillDelayed.length === 0 &&
1140                                    notAvail.length === 0) ? 0 : retryCount - 1;
1141
1142                if (tryAgain) {
1143                    this.startTimeout();
1144                }
1145
1146                this.locked = false;
1147
1148                return true;
1149
1150            },
1151
1152            /**
1153             * Removes all listeners attached to the given element via addListener.
1154             * Optionally, the node's children can also be purged.
1155             * Optionally, you can specify a specific type of event to remove.
1156             * @method purgeElement
1157             * @param {HTMLElement} el the element to purge
1158             * @param {boolean} recurse recursively purge this element's children
1159             * as well.  Use with caution.
1160             * @param {string} sType optional type of listener to purge. If
1161             * left out, all listeners will be removed
1162             */
1163            purgeElement: function(el, recurse, sType) {
1164                var elListeners = this.getListeners(el, sType);
1165                if (elListeners) {
1166                    for (var i=0,len=elListeners.length; i<len ; ++i) {
1167                        var l = elListeners[i];
1168                        // can't use the index on the changing collection
1169                        //this.removeListener(el, l.type, l.fn, l.index);
1170                        this.removeListener(el, l.type, l.fn);
1171                    }
1172                }
1173
1174                if (recurse && el && el.childNodes) {
1175                    for (i=0,len=el.childNodes.length; i<len ; ++i) {
1176                        this.purgeElement(el.childNodes[i], recurse, sType);
1177                    }
1178                }
1179            },
1180
1181            /**
1182             * Returns all listeners attached to the given element via addListener.
1183             * Optionally, you can specify a specific type of event to return.
1184             * @method getListeners
1185             * @param el {HTMLElement} the element to inspect
1186             * @param sType {string} optional type of listener to return. If
1187             * left out, all listeners will be returned
1188             * @return {Object} the listener. Contains the following fields:
1189             *    type:   (string)   the type of event
1190             *    fn:     (function) the callback supplied to addListener
1191             *    obj:    (object)   the custom object supplied to addListener
1192             *    adjust: (boolean)  whether or not to adjust the default scope
1193             *    index:  (int)      its position in the Event util listener cache
1194             */
1195            getListeners: function(el, sType) {
1196                var elListeners = [];
1197                if (listeners && listeners.length > 0) {
1198                    for (var i=0,len=listeners.length; i<len ; ++i) {
1199                        var l = listeners[i];
1200                        if ( l  && l[this.EL] === el &&
1201                                (!sType || sType === l[this.TYPE]) ) {
1202                            elListeners.push({
1203                                type:   l[this.TYPE],
1204                                fn:     l[this.FN],
1205                                obj:    l[this.SCOPE],
1206                                adjust: l[this.ADJ_SCOPE],
1207                                index:  i
1208                            });
1209                        }
1210                    }
1211                }
1212
1213                return (elListeners.length) ? elListeners : null;
1214            },
1215
1216            /**
1217             * Removes all listeners registered by pe.event.  Called
1218             * automatically during the unload event.
1219             * @method _unload
1220             * @private
1221             */
1222            _unload: function(e) {
1223
1224                var EU = YAHOO.util.Event;
1225
1226                for (var i=0,len=unloadListeners.length; i<len; ++i) {
1227                    var l = unloadListeners[i];
1228                    if (l) {
1229                        var scope = (l[EU.ADJ_SCOPE]) ? l[EU.SCOPE]: window;
1230                        l[EU.FN].call(scope, EU.getEvent(e), l[EU.SCOPE] );
1231                        delete unloadListeners[i];
1232                        l=null;
1233                    }
1234                }
1235
1236                if (listeners && listeners.length > 0) {
1237                    //for (i=0,len=listeners.length; i<len ; ++i) {
1238                    var j = listeners.length;
1239                    while (j) {
1240                        var index = j-1;
1241                        l = listeners[index];
1242                        if (l) {
1243                            EU.removeListener(l[EU.EL], l[EU.TYPE],
1244                                    l[EU.FN], index);
1245                        }
1246
1247                        l=null;
1248
1249                        j = j - 1;
1250                    }
1251
1252                    EU.clearCache();
1253                }
1254
1255                for (i=0,len=legacyEvents.length; i<len; ++i) {
1256                    // dereference the element
1257                    delete legacyEvents[i][0];
1258                    // delete the array item
1259                    delete legacyEvents[i];
1260                }
1261
1262                EU._simpleRemove(window, "unload", EU._unload);
1263
1264            },
1265
1266            /**
1267             * Returns scrollLeft
1268             * @method _getScrollLeft
1269             * @private
1270             */
1271            _getScrollLeft: function() {
1272                return this._getScroll()[1];
1273            },
1274
1275            /**
1276             * Returns scrollTop
1277             * @method _getScrollTop
1278             * @private
1279             */
1280            _getScrollTop: function() {
1281                return this._getScroll()[0];
1282            },
1283
1284            /**
1285             * Returns the scrollTop and scrollLeft.  Used to calculate the
1286             * pageX and pageY in Internet Explorer
1287             * @method _getScroll
1288             * @private
1289             */
1290            _getScroll: function() {
1291                var dd = document.documentElement, db = document.body;
1292                if (dd && (dd.scrollTop || dd.scrollLeft)) {
1293                    return [dd.scrollTop, dd.scrollLeft];
1294                } else if (db) {
1295                    return [db.scrollTop, db.scrollLeft];
1296                } else {
1297                    return [0, 0];
1298                }
1299            },
1300
1301            /**
1302             * Adds a DOM event directly without the caching, cleanup, scope adj, etc
1303             *
1304             * @param el the elment to bind the handler to
1305             * @param {string} sType the type of event handler
1306             * @param {function} fn the callback to invoke
1307             * @param {boolen} capture or bubble phase
1308             * @private
1309             */
1310            _simpleAdd: function (el, sType, fn, capture) {
1311                if (el.addEventListener) {
1312                    el.addEventListener(sType, fn, (capture));
1313                } else if (el.attachEvent) {
1314                    el.attachEvent("on" + sType, fn);
1315                }
1316            },
1317
1318            /**
1319             * Basic remove listener
1320             *
1321             * @param el the elment to bind the handler to
1322             * @param {string} sType the type of event handler
1323             * @param {function} fn the callback to invoke
1324             * @param {boolen} capture or bubble phase
1325             * @private
1326             */
1327            _simpleRemove: function (el, sType, fn, capture) {
1328                if (el.removeEventListener) {
1329                    el.removeEventListener(sType, fn, (capture));
1330                } else if (el.detachEvent) {
1331                    el.detachEvent("on" + sType, fn);
1332                }
1333            }
1334        };
1335
1336    } ();
1337
1338    /**
1339     * YAHOO.util.Event.on is an alias for addListener
1340     * @method on
1341     * @see addListener
1342     */
1343    YAHOO.util.Event.on = YAHOO.util.Event.addListener;
1344
1345    if (document && document.body) {
1346        YAHOO.util.Event._load();
1347    } else {
1348        YAHOO.util.Event._simpleAdd(window, "load", YAHOO.util.Event._load);
1349    }
1350    YAHOO.util.Event._simpleAdd(window, "unload", YAHOO.util.Event._unload);
1351    YAHOO.util.Event._tryPreloadAttach();
1352}
1353
Note: See TracBrowser for help on using the repository browser.