Project

General

Profile

1
/*
2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 2.8.0r4
6
*/
7

    
8
/**
9
 * The CustomEvent class lets you define events for your application
10
 * that can be subscribed to by one or more independent component.
11
 *
12
 * @param {String}  type The type of event, which is passed to the callback
13
 *                  when the event fires
14
 * @param {Object}  context The context the event will fire from.  "this" will
15
 *                  refer to this object in the callback.  Default value: 
16
 *                  the window object.  The listener can override this.
17
 * @param {boolean} silent pass true to prevent the event from writing to
18
 *                  the debugsystem
19
 * @param {int}     signature the signature that the custom event subscriber
20
 *                  will receive. YAHOO.util.CustomEvent.LIST or 
21
 *                  YAHOO.util.CustomEvent.FLAT.  The default is
22
 *                  YAHOO.util.CustomEvent.LIST.
23
 * @param fireOnce {boolean} If configured to fire once, the custom event 
24
 * will only notify subscribers a single time regardless of how many times 
25
 * the event is fired.  In addition, new subscribers will be notified 
26
 * immediately if the event has already been fired.
27
 * @namespace YAHOO.util
28
 * @class CustomEvent
29
 * @constructor
30
 */
31
YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) {
32

    
33
    /**
34
     * The type of event, returned to subscribers when the event fires
35
     * @property type
36
     * @type string
37
     */
38
    this.type = type;
39

    
40
    /**
41
     * The context the event will fire from by default. Defaults to the window obj.
42
     * @property scope
43
     * @type object
44
     */
45
    this.scope = context || window;
46

    
47
    /**
48
     * By default all custom events are logged in the debug build. Set silent to true 
49
     * to disable debug output for this event.
50
     * @property silent
51
     * @type boolean
52
     */
53
    this.silent = silent;
54

    
55
    /**
56
     * If configured to fire once, the custom event will only notify subscribers
57
     * a single time regardless of how many times the event is fired.  In addition,
58
     * new subscribers will be notified immediately if the event has already been
59
     * fired.
60
     * @property fireOnce
61
     * @type boolean
62
     * @default false
63
     */
64
    this.fireOnce = fireOnce;
65

    
66
    /**
67
     * Indicates whether or not this event has ever been fired.
68
     * @property fired
69
     * @type boolean
70
     * @default false
71
     */
72
    this.fired = false;
73

    
74
    /**
75
     * For fireOnce events the arguments the event was fired with are stored
76
     * so that new subscribers get the proper payload.
77
     * @property firedWith
78
     * @type Array
79
     */
80
    this.firedWith = null;
81

    
82
    /**
83
     * Custom events support two styles of arguments provided to the event
84
     * subscribers.  
85
     * <ul>
86
     * <li>YAHOO.util.CustomEvent.LIST: 
87
     *   <ul>
88
     *   <li>param1: event name</li>
89
     *   <li>param2: array of arguments sent to fire</li>
90
     *   <li>param3: <optional> a custom object supplied by the subscriber</li>
91
     *   </ul>
92
     * </li>
93
     * <li>YAHOO.util.CustomEvent.FLAT
94
     *   <ul>
95
     *   <li>param1: the first argument passed to fire.  If you need to
96
     *           pass multiple parameters, use and array or object literal</li>
97
     *   <li>param2: <optional> a custom object supplied by the subscriber</li>
98
     *   </ul>
99
     * </li>
100
     * </ul>
101
     *   @property signature
102
     *   @type int
103
     */
104
    this.signature = signature || YAHOO.util.CustomEvent.LIST;
105

    
106
    /**
107
     * The subscribers to this event
108
     * @property subscribers
109
     * @type Subscriber[]
110
     */
111
    this.subscribers = [];
112

    
113
    if (!this.silent) {
114
        YAHOO.log( "Creating " + this, "info", "Event" );
115
    }
116

    
117
    var onsubscribeType = "_YUICEOnSubscribe";
118

    
119
    // Only add subscribe events for events that are not generated by 
120
    // CustomEvent
121
    if (type !== onsubscribeType) {
122

    
123
        /**
124
         * Custom events provide a custom event that fires whenever there is
125
         * a new subscriber to the event.  This provides an opportunity to
126
         * handle the case where there is a non-repeating event that has
127
         * already fired has a new subscriber.  
128
         *
129
         * @event subscribeEvent
130
         * @type YAHOO.util.CustomEvent
131
         * @param fn {Function} The function to execute
132
         * @param obj <Object> An object to be passed along when the event fires. 
133
         * Defaults to the custom event.
134
         * @param override <boolean|Object> If true, the obj passed in becomes the 
135
         * execution context of the listener. If an object, that object becomes 
136
         * the execution context. Defaults to the custom event.
137
         */
138
        this.subscribeEvent = 
139
                new YAHOO.util.CustomEvent(onsubscribeType, this, true);
140

    
141
    } 
142

    
143

    
144
    /**
145
     * In order to make it possible to execute the rest of the subscriber
146
     * stack when one thows an exception, the subscribers exceptions are
147
     * caught.  The most recent exception is stored in this property
148
     * @property lastError
149
     * @type Error
150
     */
151
    this.lastError = null;
152
};
153

    
154
/**
155
 * Subscriber listener sigature constant.  The LIST type returns three
156
 * parameters: the event type, the array of args passed to fire, and
157
 * the optional custom object
158
 * @property YAHOO.util.CustomEvent.LIST
159
 * @static
160
 * @type int
161
 */
162
YAHOO.util.CustomEvent.LIST = 0;
163

    
164
/**
165
 * Subscriber listener sigature constant.  The FLAT type returns two
166
 * parameters: the first argument passed to fire and the optional 
167
 * custom object
168
 * @property YAHOO.util.CustomEvent.FLAT
169
 * @static
170
 * @type int
171
 */
172
YAHOO.util.CustomEvent.FLAT = 1;
173

    
174
YAHOO.util.CustomEvent.prototype = {
175

    
176
    /**
177
     * Subscribes the caller to this event
178
     * @method subscribe
179
     * @param {Function} fn        The function to execute
180
     * @param {Object}   obj       An object to be passed along when the event fires.
181
     * overrideContext <boolean|Object> If true, the obj passed in becomes the execution 
182
     * context of the listener. If an object, that object becomes the execution context.
183
     */
184
    subscribe: function(fn, obj, overrideContext) {
185

    
186
        if (!fn) {
187
throw new Error("Invalid callback for subscriber to '" + this.type + "'");
188
        }
189

    
190
        if (this.subscribeEvent) {
191
            this.subscribeEvent.fire(fn, obj, overrideContext);
192
        }
193

    
194
        var s = new YAHOO.util.Subscriber(fn, obj, overrideContext);
195

    
196
        if (this.fireOnce && this.fired) {
197
            this.notify(s, this.firedWith);
198
        } else {
199
            this.subscribers.push(s);
200
        }
201
    },
202

    
203
    /**
204
     * Unsubscribes subscribers.
205
     * @method unsubscribe
206
     * @param {Function} fn  The subscribed function to remove, if not supplied
207
     *                       all will be removed
208
     * @param {Object}   obj  The custom object passed to subscribe.  This is
209
     *                        optional, but if supplied will be used to
210
     *                        disambiguate multiple listeners that are the same
211
     *                        (e.g., you subscribe many object using a function
212
     *                        that lives on the prototype)
213
     * @return {boolean} True if the subscriber was found and detached.
214
     */
215
    unsubscribe: function(fn, obj) {
216

    
217
        if (!fn) {
218
            return this.unsubscribeAll();
219
        }
220

    
221
        var found = false;
222
        for (var i=0, len=this.subscribers.length; i<len; ++i) {
223
            var s = this.subscribers[i];
224
            if (s && s.contains(fn, obj)) {
225
                this._delete(i);
226
                found = true;
227
            }
228
        }
229

    
230
        return found;
231
    },
232

    
233
    /**
234
     * Notifies the subscribers.  The callback functions will be executed
235
     * from the context specified when the event was created, and with the 
236
     * following parameters:
237
     *   <ul>
238
     *   <li>The type of event</li>
239
     *   <li>All of the arguments fire() was executed with as an array</li>
240
     *   <li>The custom object (if any) that was passed into the subscribe() 
241
     *       method</li>
242
     *   </ul>
243
     * @method fire 
244
     * @param {Object*} arguments an arbitrary set of parameters to pass to 
245
     *                            the handler.
246
     * @return {boolean} false if one of the subscribers returned false, 
247
     *                   true otherwise
248
     */
249
    fire: function() {
250

    
251
        this.lastError = null;
252

    
253
        var errors = [],
254
            len=this.subscribers.length;
255

    
256

    
257
        var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false;
258

    
259
        if (this.fireOnce) {
260
            if (this.fired) {
261
                YAHOO.log('fireOnce event has already fired: ' + this.type);
262
                return true;
263
            } else {
264
                this.firedWith = args;
265
            }
266
        }
267

    
268
        this.fired = true;
269

    
270
        if (!len && this.silent) {
271
            //YAHOO.log('DEBUG no subscribers');
272
            return true;
273
        }
274

    
275
        if (!this.silent) {
276
            YAHOO.log( "Firing "       + this  + ", " + 
277
                       "args: "        + args  + ", " +
278
                       "subscribers: " + len,                 
279
                       "info", "Event"                  );
280
        }
281

    
282
        // make a copy of the subscribers so that there are
283
        // no index problems if one subscriber removes another.
284
        var subs = this.subscribers.slice();
285

    
286
        for (i=0; i<len; ++i) {
287
            var s = subs[i];
288
            if (!s) {
289
                rebuild=true;
290
            } else {
291

    
292
                ret = this.notify(s, args);
293

    
294
                if (false === ret) {
295
                    if (!this.silent) {
296
                        YAHOO.log("Event stopped, sub " + i + " of " + len, "info", "Event");
297
                    }
298

    
299
                    break;
300
                }
301
            }
302
        }
303

    
304
        return (ret !== false);
305
    },
306

    
307
    notify: function(s, args) {
308

    
309
        var ret, param=null, scope = s.getScope(this.scope),
310
                 throwErrors = YAHOO.util.Event.throwErrors;
311

    
312
        if (!this.silent) {
313
            YAHOO.log( this.type + "-> " + s, "info", "Event" );
314
        }
315

    
316
        if (this.signature == YAHOO.util.CustomEvent.FLAT) {
317

    
318
            if (args.length > 0) {
319
                param = args[0];
320
            }
321

    
322
            try {
323
                ret = s.fn.call(scope, param, s.obj);
324
            } catch(e) {
325
                this.lastError = e;
326
                // errors.push(e);
327
                YAHOO.log(this + " subscriber exception: " + e, "error", "Event");
328
                if (throwErrors) {
329
                    throw e;
330
                }
331
            }
332
        } else {
333
            try {
334
                ret = s.fn.call(scope, this.type, args, s.obj);
335
            } catch(ex) {
336
                this.lastError = ex;
337
                YAHOO.log(this + " subscriber exception: " + ex, "error", "Event");
338
                if (throwErrors) {
339
                    throw ex;
340
                }
341
            }
342
        }
343

    
344
        return ret;
345
    },
346

    
347
    /**
348
     * Removes all listeners
349
     * @method unsubscribeAll
350
     * @return {int} The number of listeners unsubscribed
351
     */
352
    unsubscribeAll: function() {
353
        var l = this.subscribers.length, i;
354
        for (i=l-1; i>-1; i--) {
355
            this._delete(i);
356
        }
357

    
358
        this.subscribers=[];
359

    
360
        return l;
361
    },
362

    
363
    /**
364
     * @method _delete
365
     * @private
366
     */
367
    _delete: function(index) {
368
        var s = this.subscribers[index];
369
        if (s) {
370
            delete s.fn;
371
            delete s.obj;
372
        }
373

    
374
        // this.subscribers[index]=null;
375
        this.subscribers.splice(index, 1);
376
    },
377

    
378
    /**
379
     * @method toString
380
     */
381
    toString: function() {
382
         return "CustomEvent: " + "'" + this.type  + "', " + 
383
             "context: " + this.scope;
384

    
385
    }
386
};
387

    
388
/////////////////////////////////////////////////////////////////////
389

    
390
/**
391
 * Stores the subscriber information to be used when the event fires.
392
 * @param {Function} fn       The function to execute
393
 * @param {Object}   obj      An object to be passed along when the event fires
394
 * @param {boolean}  overrideContext If true, the obj passed in becomes the execution
395
 *                            context of the listener
396
 * @class Subscriber
397
 * @constructor
398
 */
399
YAHOO.util.Subscriber = function(fn, obj, overrideContext) {
400

    
401
    /**
402
     * The callback that will be execute when the event fires
403
     * @property fn
404
     * @type function
405
     */
406
    this.fn = fn;
407

    
408
    /**
409
     * An optional custom object that will passed to the callback when
410
     * the event fires
411
     * @property obj
412
     * @type object
413
     */
414
    this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
415

    
416
    /**
417
     * The default execution context for the event listener is defined when the
418
     * event is created (usually the object which contains the event).
419
     * By setting overrideContext to true, the execution context becomes the custom
420
     * object passed in by the subscriber.  If overrideContext is an object, that 
421
     * object becomes the context.
422
     * @property overrideContext
423
     * @type boolean|object
424
     */
425
    this.overrideContext = overrideContext;
426

    
427
};
428

    
429
/**
430
 * Returns the execution context for this listener.  If overrideContext was set to true
431
 * the custom obj will be the context.  If overrideContext is an object, that is the
432
 * context, otherwise the default context will be used.
433
 * @method getScope
434
 * @param {Object} defaultScope the context to use if this listener does not
435
 *                              override it.
436
 */
437
YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
438
    if (this.overrideContext) {
439
        if (this.overrideContext === true) {
440
            return this.obj;
441
        } else {
442
            return this.overrideContext;
443
        }
444
    }
445
    return defaultScope;
446
};
447

    
448
/**
449
 * Returns true if the fn and obj match this objects properties.
450
 * Used by the unsubscribe method to match the right subscriber.
451
 *
452
 * @method contains
453
 * @param {Function} fn the function to execute
454
 * @param {Object} obj an object to be passed along when the event fires
455
 * @return {boolean} true if the supplied arguments match this 
456
 *                   subscriber's signature.
457
 */
458
YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
459
    if (obj) {
460
        return (this.fn == fn && this.obj == obj);
461
    } else {
462
        return (this.fn == fn);
463
    }
464
};
465

    
466
/**
467
 * @method toString
468
 */
469
YAHOO.util.Subscriber.prototype.toString = function() {
470
    return "Subscriber { obj: " + this.obj  + 
471
           ", overrideContext: " +  (this.overrideContext || "no") + " }";
472
};
473

    
474
/**
475
 * The Event Utility provides utilities for managing DOM Events and tools
476
 * for building event systems
477
 *
478
 * @module event
479
 * @title Event Utility
480
 * @namespace YAHOO.util
481
 * @requires yahoo
482
 */
483

    
484
// The first instance of Event will win if it is loaded more than once.
485
// @TODO this needs to be changed so that only the state data that needs to
486
// be preserved is kept, while methods are overwritten/added as needed.
487
// This means that the module pattern can't be used.
488
if (!YAHOO.util.Event) {
489

    
490
/**
491
 * The event utility provides functions to add and remove event listeners,
492
 * event cleansing.  It also tries to automatically remove listeners it
493
 * registers during the unload event.
494
 *
495
 * @class Event
496
 * @static
497
 */
498
    YAHOO.util.Event = function() {
499

    
500
        /**
501
         * True after the onload event has fired
502
         * @property loadComplete
503
         * @type boolean
504
         * @static
505
         * @private
506
         */
507
        var loadComplete =  false,
508

    
509
        /**
510
         * Cache of wrapped listeners
511
         * @property listeners
512
         * @type array
513
         * @static
514
         * @private
515
         */
516
        listeners = [],
517

    
518

    
519
        /**
520
         * User-defined unload function that will be fired before all events
521
         * are detached
522
         * @property unloadListeners
523
         * @type array
524
         * @static
525
         * @private
526
         */
527
        unloadListeners = [],
528

    
529
        /**
530
         * The number of times to poll after window.onload.  This number is
531
         * increased if additional late-bound handlers are requested after
532
         * the page load.
533
         * @property retryCount
534
         * @static
535
         * @private
536
         */
537
        retryCount = 0,
538

    
539
        /**
540
         * onAvailable listeners
541
         * @property onAvailStack
542
         * @static
543
         * @private
544
         */
545
        onAvailStack = [],
546

    
547
        /**
548
         * Counter for auto id generation
549
         * @property counter
550
         * @static
551
         * @private
552
         */
553
        counter = 0,
554
        
555
        /**
556
         * Normalized keycodes for webkit/safari
557
         * @property webkitKeymap
558
         * @type {int: int}
559
         * @private
560
         * @static
561
         * @final
562
         */
563
         webkitKeymap = {
564
            63232: 38, // up
565
            63233: 40, // down
566
            63234: 37, // left
567
            63235: 39, // right
568
            63276: 33, // page up
569
            63277: 34, // page down
570
            25: 9      // SHIFT-TAB (Safari provides a different key code in
571
                       // this case, even though the shiftKey modifier is set)
572
        },
573

    
574
		isIE = YAHOO.env.ua.ie,
575

    
576
        // String constants used by the addFocusListener and removeFocusListener methods
577
		
578
       	FOCUSIN = "focusin",
579
       	FOCUSOUT = "focusout";
580

    
581
        return {
582

    
583
            /**
584
             * The number of times we should look for elements that are not
585
             * in the DOM at the time the event is requested after the document
586
             * has been loaded.  The default is 500@amp;40 ms, so it will poll
587
             * for 20 seconds or until all outstanding handlers are bound
588
             * (whichever comes first).
589
             * @property POLL_RETRYS
590
             * @type int
591
             * @static
592
             * @final
593
             */
594
            POLL_RETRYS: 500,
595

    
596
            /**
597
             * The poll interval in milliseconds
598
             * @property POLL_INTERVAL
599
             * @type int
600
             * @static
601
             * @final
602
             */
603
            POLL_INTERVAL: 40,
604

    
605
            /**
606
             * Element to bind, int constant
607
             * @property EL
608
             * @type int
609
             * @static
610
             * @final
611
             */
612
            EL: 0,
613

    
614
            /**
615
             * Type of event, int constant
616
             * @property TYPE
617
             * @type int
618
             * @static
619
             * @final
620
             */
621
            TYPE: 1,
622

    
623
            /**
624
             * Function to execute, int constant
625
             * @property FN
626
             * @type int
627
             * @static
628
             * @final
629
             */
630
            FN: 2,
631

    
632
            /**
633
             * Function wrapped for context correction and cleanup, int constant
634
             * @property WFN
635
             * @type int
636
             * @static
637
             * @final
638
             */
639
            WFN: 3,
640

    
641
            /**
642
             * Object passed in by the user that will be returned as a 
643
             * parameter to the callback, int constant.  Specific to
644
             * unload listeners
645
             * @property OBJ
646
             * @type int
647
             * @static
648
             * @final
649
             */
650
            UNLOAD_OBJ: 3,
651

    
652
            /**
653
             * Adjusted context, either the element we are registering the event
654
             * on or the custom object passed in by the listener, int constant
655
             * @property ADJ_SCOPE
656
             * @type int
657
             * @static
658
             * @final
659
             */
660
            ADJ_SCOPE: 4,
661

    
662
            /**
663
             * The original obj passed into addListener
664
             * @property OBJ
665
             * @type int
666
             * @static
667
             * @final
668
             */
669
            OBJ: 5,
670

    
671
            /**
672
             * The original context parameter passed into addListener
673
             * @property OVERRIDE
674
             * @type int
675
             * @static
676
             * @final
677
             */
678
            OVERRIDE: 6,
679

    
680
            /**
681
             * The original capture parameter passed into addListener
682
             * @property CAPTURE
683
             * @type int
684
             * @static
685
             * @final
686
             */
687
			CAPTURE: 7,
688

    
689
            /**
690
             * addListener/removeListener can throw errors in unexpected scenarios.
691
             * These errors are suppressed, the method returns false, and this property
692
             * is set
693
             * @property lastError
694
             * @static
695
             * @type Error
696
             */
697
            lastError: null,
698

    
699
            /**
700
             * Safari detection
701
             * @property isSafari
702
             * @private
703
             * @static
704
             * @deprecated use YAHOO.env.ua.webkit
705
             */
706
            isSafari: YAHOO.env.ua.webkit,
707
            
708
            /**
709
             * webkit version
710
             * @property webkit
711
             * @type string
712
             * @private
713
             * @static
714
             * @deprecated use YAHOO.env.ua.webkit
715
             */
716
            webkit: YAHOO.env.ua.webkit,
717
            
718
            /**
719
             * IE detection 
720
             * @property isIE
721
             * @private
722
             * @static
723
             * @deprecated use YAHOO.env.ua.ie
724
             */
725
            isIE: isIE,
726

    
727
            /**
728
             * poll handle
729
             * @property _interval
730
             * @static
731
             * @private
732
             */
733
            _interval: null,
734

    
735
            /**
736
             * document readystate poll handle
737
             * @property _dri
738
             * @static
739
             * @private
740
             */
741
             _dri: null,
742

    
743

    
744
            /**
745
             * Map of special event types
746
             * @property _specialTypes
747
             * @static
748
             * @private
749
             */
750
			_specialTypes: {
751
				focusin: (isIE ? "focusin" : "focus"),
752
				focusout: (isIE ? "focusout" : "blur")
753
			},
754

    
755

    
756
            /**
757
             * True when the document is initially usable
758
             * @property DOMReady
759
             * @type boolean
760
             * @static
761
             */
762
            DOMReady: false,
763

    
764
            /**
765
             * Errors thrown by subscribers of custom events are caught
766
             * and the error message is written to the debug console.  If
767
             * this property is set to true, it will also re-throw the
768
             * error.
769
             * @property throwErrors
770
             * @type boolean
771
             * @default false
772
             */
773
            throwErrors: false,
774

    
775

    
776
            /**
777
             * @method startInterval
778
             * @static
779
             * @private
780
             */
781
            startInterval: function() {
782
                if (!this._interval) {
783
                    // var self = this;
784
                    // var callback = function() { self._tryPreloadAttach(); };
785
                    // this._interval = setInterval(callback, this.POLL_INTERVAL);
786
                    this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true);
787
                }
788
            },
789

    
790
            /**
791
             * Executes the supplied callback when the item with the supplied
792
             * id is found.  This is meant to be used to execute behavior as
793
             * soon as possible as the page loads.  If you use this after the
794
             * initial page load it will poll for a fixed time for the element.
795
             * The number of times it will poll and the frequency are
796
             * configurable.  By default it will poll for 10 seconds.
797
             *
798
             * <p>The callback is executed with a single parameter:
799
             * the custom object parameter, if provided.</p>
800
             *
801
             * @method onAvailable
802
             *
803
             * @param {string||string[]}   id the id of the element, or an array
804
             * of ids to look for.
805
             * @param {function} fn what to execute when the element is found.
806
             * @param {object}   obj an optional object to be passed back as
807
             *                   a parameter to fn.
808
             * @param {boolean|object}  overrideContext If set to true, fn will execute
809
             *                   in the context of obj, if set to an object it
810
             *                   will execute in the context of that object
811
             * @param checkContent {boolean} check child node readiness (onContentReady)
812
             * @static
813
             */
814
            onAvailable: function(id, fn, obj, overrideContext, checkContent) {
815

    
816
                var a = (YAHOO.lang.isString(id)) ? [id] : id;
817

    
818
                for (var i=0; i<a.length; i=i+1) {
819
                    onAvailStack.push({id:         a[i], 
820
                                       fn:         fn, 
821
                                       obj:        obj, 
822
                                       overrideContext:   overrideContext, 
823
                                       checkReady: checkContent });
824
                }
825

    
826
                retryCount = this.POLL_RETRYS;
827

    
828
                this.startInterval();
829
            },
830

    
831
            /**
832
             * Works the same way as onAvailable, but additionally checks the
833
             * state of sibling elements to determine if the content of the
834
             * available element is safe to modify.
835
             *
836
             * <p>The callback is executed with a single parameter:
837
             * the custom object parameter, if provided.</p>
838
             *
839
             * @method onContentReady
840
             *
841
             * @param {string}   id the id of the element to look for.
842
             * @param {function} fn what to execute when the element is ready.
843
             * @param {object}   obj an optional object to be passed back as
844
             *                   a parameter to fn.
845
             * @param {boolean|object}  overrideContext If set to true, fn will execute
846
             *                   in the context of obj.  If an object, fn will
847
             *                   exectute in the context of that object
848
             *
849
             * @static
850
             */
851
            onContentReady: function(id, fn, obj, overrideContext) {
852
                this.onAvailable(id, fn, obj, overrideContext, true);
853
            },
854

    
855
            /**
856
             * Executes the supplied callback when the DOM is first usable.  This
857
             * will execute immediately if called after the DOMReady event has
858
             * fired.   @todo the DOMContentReady event does not fire when the
859
             * script is dynamically injected into the page.  This means the
860
             * DOMReady custom event will never fire in FireFox or Opera when the
861
             * library is injected.  It _will_ fire in Safari, and the IE 
862
             * implementation would allow for us to fire it if the defered script
863
             * is not available.  We want this to behave the same in all browsers.
864
             * Is there a way to identify when the script has been injected 
865
             * instead of included inline?  Is there a way to know whether the 
866
             * window onload event has fired without having had a listener attached 
867
             * to it when it did so?
868
             *
869
             * <p>The callback is a CustomEvent, so the signature is:</p>
870
             * <p>type &lt;string&gt;, args &lt;array&gt;, customobject &lt;object&gt;</p>
871
             * <p>For DOMReady events, there are no fire argments, so the
872
             * signature is:</p>
873
             * <p>"DOMReady", [], obj</p>
874
             *
875
             *
876
             * @method onDOMReady
877
             *
878
             * @param {function} fn what to execute when the element is found.
879
             * @param {object}   obj an optional object to be passed back as
880
             *                   a parameter to fn.
881
             * @param {boolean|object}  overrideContext If set to true, fn will execute
882
             *                   in the context of obj, if set to an object it
883
             *                   will execute in the context of that object
884
             *
885
             * @static
886
             */
887
            // onDOMReady: function(fn, obj, overrideContext) {
888
            onDOMReady: function() {
889
                this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments);
890
            },
891

    
892

    
893
            /**
894
             * Appends an event handler
895
             *
896
             * @method _addListener
897
             *
898
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
899
             *  reference, or a collection of ids and/or elements to assign the 
900
             *  listener to.
901
             * @param {String}   sType     The type of event to append
902
             * @param {Function} fn        The method the event invokes
903
             * @param {Object}   obj    An arbitrary object that will be 
904
             *                             passed as a parameter to the handler
905
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
906
             *                             the execution context of the listener. If an
907
             *                             object, this object becomes the execution
908
             *                             context.
909
             * @param {boolen}      capture capture or bubble phase
910
             * @return {Boolean} True if the action was successful or defered,
911
             *                        false if one or more of the elements 
912
             *                        could not have the listener attached,
913
             *                        or if the operation throws an exception.
914
             * @private
915
             * @static
916
             */
917
            _addListener: function(el, sType, fn, obj, overrideContext, bCapture) {
918

    
919
                if (!fn || !fn.call) {
920
                    YAHOO.log(sType + " addListener failed, invalid callback", "error", "Event");
921
                    return false;
922
                }
923

    
924
                // The el argument can be an array of elements or element ids.
925
                if ( this._isValidCollection(el)) {
926
                    var ok = true;
927
                    for (var i=0,len=el.length; i<len; ++i) {
928
                        ok = this.on(el[i], 
929
                                       sType, 
930
                                       fn, 
931
                                       obj, 
932
                                       overrideContext) && ok;
933
                    }
934
                    return ok;
935

    
936
                } else if (YAHOO.lang.isString(el)) {
937
                    var oEl = this.getEl(el);
938
                    // If the el argument is a string, we assume it is 
939
                    // actually the id of the element.  If the page is loaded
940
                    // we convert el to the actual element, otherwise we 
941
                    // defer attaching the event until onload event fires
942

    
943
                    // check to see if we need to delay hooking up the event 
944
                    // until after the page loads.
945
                    if (oEl) {
946
                        el = oEl;
947
                    } else {
948
                        // defer adding the event until the element is available
949
                        this.onAvailable(el, function() {
950
                           YAHOO.util.Event._addListener(el, sType, fn, obj, overrideContext, bCapture);
951
                        });
952

    
953
                        return true;
954
                    }
955
                }
956

    
957
                // Element should be an html element or an array if we get 
958
                // here.
959
                if (!el) {
960
                    // this.logger.debug("unable to attach event " + sType);
961
                    return false;
962
                }
963

    
964
                // we need to make sure we fire registered unload events 
965
                // prior to automatically unhooking them.  So we hang on to 
966
                // these instead of attaching them to the window and fire the
967
                // handles explicitly during our one unload event.
968
                if ("unload" == sType && obj !== this) {
969
                    unloadListeners[unloadListeners.length] =
970
                            [el, sType, fn, obj, overrideContext];
971
                    return true;
972
                }
973

    
974
                // this.logger.debug("Adding handler: " + el + ", " + sType);
975

    
976
                // if the user chooses to override the context, we use the custom
977
                // object passed in, otherwise the executing context will be the
978
                // HTML element that the event is registered on
979
                var context = el;
980
                if (overrideContext) {
981
                    if (overrideContext === true) {
982
                        context = obj;
983
                    } else {
984
                        context = overrideContext;
985
                    }
986
                }
987

    
988
                // wrap the function so we can return the obj object when
989
                // the event fires;
990
                var wrappedFn = function(e) {
991
                        return fn.call(context, YAHOO.util.Event.getEvent(e, el), 
992
                                obj);
993
                    };
994

    
995
                var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture];
996
                var index = listeners.length;
997
                // cache the listener so we can try to automatically unload
998
                listeners[index] = li;
999

    
1000
                try {
1001
                    this._simpleAdd(el, sType, wrappedFn, bCapture);
1002
                } catch(ex) {
1003
                    // handle an error trying to attach an event.  If it fails
1004
                    // we need to clean up the cache
1005
                    this.lastError = ex;
1006
                    this.removeListener(el, sType, fn);
1007
                    return false;
1008
                }
1009

    
1010
                return true;
1011
                
1012
            },
1013

    
1014
            /**
1015
             * Checks to see if the type requested is a special type 
1016
			 * (as defined by the _specialTypes hash), and (if so) returns 
1017
			 * the special type name.
1018
             *
1019
             * @method _getType
1020
             *
1021
             * @param {String}   sType     The type to look up
1022
             * @private
1023
             */
1024
			_getType: function (type) {
1025
			
1026
				return this._specialTypes[type] || type;
1027
				
1028
			},
1029

    
1030

    
1031
            /**
1032
             * Appends an event handler
1033
             *
1034
             * @method addListener
1035
             *
1036
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1037
             *  reference, or a collection of ids and/or elements to assign the 
1038
             *  listener to.
1039
             * @param {String}   sType     The type of event to append
1040
             * @param {Function} fn        The method the event invokes
1041
             * @param {Object}   obj    An arbitrary object that will be 
1042
             *                             passed as a parameter to the handler
1043
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
1044
             *                             the execution context of the listener. If an
1045
             *                             object, this object becomes the execution
1046
             *                             context.
1047
             * @return {Boolean} True if the action was successful or defered,
1048
             *                        false if one or more of the elements 
1049
             *                        could not have the listener attached,
1050
             *                        or if the operation throws an exception.
1051
             * @static
1052
             */
1053
            addListener: function (el, sType, fn, obj, overrideContext) {
1054

    
1055
				var capture = ((sType == FOCUSIN || sType == FOCUSOUT) && !YAHOO.env.ua.ie) ? true : false;
1056

    
1057
                return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture);
1058

    
1059
        	},
1060

    
1061

    
1062
            /**
1063
             * Attaches a focusin event listener to the specified element for 
1064
 			 * the purpose of listening for the focus event on the element's 
1065
             * descendants.
1066
             * @method addFocusListener
1067
             *
1068
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1069
             *  reference, or a collection of ids and/or elements to assign the 
1070
             *  listener to.
1071
             * @param {Function} fn        The method the event invokes
1072
             * @param {Object}   obj    An arbitrary object that will be 
1073
             *                             passed as a parameter to the handler
1074
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
1075
             *                             the execution context of the listener. If an
1076
             *                             object, this object becomes the execution
1077
             *                             context.
1078
             * @return {Boolean} True if the action was successful or defered,
1079
             *                        false if one or more of the elements 
1080
             *                        could not have the listener attached,
1081
             *                        or if the operation throws an exception.
1082
             * @static
1083
			* @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
1084
             */
1085
            addFocusListener: function (el, fn, obj, overrideContext) {
1086
                return this.on(el, FOCUSIN, fn, obj, overrideContext);
1087
            },          
1088

    
1089

    
1090
            /**
1091
             * Removes a focusin event listener to the specified element for 
1092
			 * the purpose of listening for the focus event on the element's 
1093
             * descendants.
1094
             *
1095
             * @method removeFocusListener
1096
             *
1097
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1098
             *  reference, or a collection of ids and/or elements to remove
1099
             *  the listener from.
1100
             * @param {Function} fn the method the event invokes.  If fn is
1101
             *  undefined, then all event handlers for the type of event are 
1102
             *  removed.
1103
             * @return {boolean} true if the unbind was successful, false 
1104
             *  otherwise.
1105
             * @static
1106
         	 * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type.
1107
             */
1108
            removeFocusListener: function (el, fn) { 
1109
                return this.removeListener(el, FOCUSIN, fn);
1110
            },
1111

    
1112
            /**
1113
             * Attaches a focusout event listener to the specified element for 
1114
			 * the purpose of listening for the blur event on the element's 
1115
			 * descendants.
1116
             *
1117
             * @method addBlurListener
1118
             *
1119
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1120
             *  reference, or a collection of ids and/or elements to assign the 
1121
             *  listener to.
1122
             * @param {Function} fn        The method the event invokes
1123
             * @param {Object}   obj    An arbitrary object that will be 
1124
             *                             passed as a parameter to the handler
1125
             * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
1126
             *                             the execution context of the listener. If an
1127
             *                             object, this object becomes the execution
1128
             *                             context.
1129
             * @return {Boolean} True if the action was successful or defered,
1130
             *                        false if one or more of the elements 
1131
             *                        could not have the listener attached,
1132
             *                        or if the operation throws an exception.
1133
             * @static
1134
         	 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
1135
             */
1136
            addBlurListener: function (el, fn, obj, overrideContext) {
1137
                return this.on(el, FOCUSOUT, fn, obj, overrideContext);
1138
            },          
1139

    
1140
            /**
1141
             * Removes a focusout event listener to the specified element for 
1142
			 * the purpose of listening for the blur event on the element's 
1143
			 * descendants.
1144
             *
1145
             * @method removeBlurListener
1146
             *
1147
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1148
             *  reference, or a collection of ids and/or elements to remove
1149
             *  the listener from.
1150
             * @param {Function} fn the method the event invokes.  If fn is
1151
             *  undefined, then all event handlers for the type of event are 
1152
             *  removed.
1153
             * @return {boolean} true if the unbind was successful, false 
1154
             *  otherwise.
1155
             * @static
1156
         	 * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type.
1157
             */
1158
            removeBlurListener: function (el, fn) { 
1159
                return this.removeListener(el, FOCUSOUT, fn);
1160
            },
1161

    
1162
            /**
1163
             * Removes an event listener
1164
             *
1165
             * @method removeListener
1166
             *
1167
             * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1168
             *  reference, or a collection of ids and/or elements to remove
1169
             *  the listener from.
1170
             * @param {String} sType the type of event to remove.
1171
             * @param {Function} fn the method the event invokes.  If fn is
1172
             *  undefined, then all event handlers for the type of event are 
1173
             *  removed.
1174
             * @return {boolean} true if the unbind was successful, false 
1175
             *  otherwise.
1176
             * @static
1177
             */
1178
            removeListener: function(el, sType, fn) {
1179
                var i, len, li;
1180

    
1181
				sType = this._getType(sType);
1182

    
1183
                // The el argument can be a string
1184
                if (typeof el == "string") {
1185
                    el = this.getEl(el);
1186
                // The el argument can be an array of elements or element ids.
1187
                } else if ( this._isValidCollection(el)) {
1188
                    var ok = true;
1189
                    for (i=el.length-1; i>-1; i--) {
1190
                        ok = ( this.removeListener(el[i], sType, fn) && ok );
1191
                    }
1192
                    return ok;
1193
                }
1194

    
1195
                if (!fn || !fn.call) {
1196
                    // this.logger.debug("Error, function is not valid " + fn);
1197
                    //return false;
1198
                    return this.purgeElement(el, false, sType);
1199
                }
1200

    
1201
                if ("unload" == sType) {
1202

    
1203
                    for (i=unloadListeners.length-1; i>-1; i--) {
1204
                        li = unloadListeners[i];
1205
                        if (li && 
1206
                            li[0] == el && 
1207
                            li[1] == sType && 
1208
                            li[2] == fn) {
1209
                                unloadListeners.splice(i, 1);
1210
                                // unloadListeners[i]=null;
1211
                                return true;
1212
                        }
1213
                    }
1214

    
1215
                    return false;
1216
                }
1217

    
1218
                var cacheItem = null;
1219

    
1220
                // The index is a hidden parameter; needed to remove it from
1221
                // the method signature because it was tempting users to
1222
                // try and take advantage of it, which is not possible.
1223
                var index = arguments[3];
1224
  
1225
                if ("undefined" === typeof index) {
1226
                    index = this._getCacheIndex(listeners, el, sType, fn);
1227
                }
1228

    
1229
                if (index >= 0) {
1230
                    cacheItem = listeners[index];
1231
                }
1232

    
1233
                if (!el || !cacheItem) {
1234
                    // this.logger.debug("cached listener not found");
1235
                    return false;
1236
                }
1237

    
1238
                // this.logger.debug("Removing handler: " + el + ", " + sType);
1239

    
1240
				var bCapture = cacheItem[this.CAPTURE] === true ? true : false;
1241

    
1242
                try {
1243
                    this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture);
1244
                } catch(ex) {
1245
                    this.lastError = ex;
1246
                    return false;
1247
                }
1248

    
1249
                // removed the wrapped handler
1250
                delete listeners[index][this.WFN];
1251
                delete listeners[index][this.FN];
1252
                listeners.splice(index, 1);
1253
                // listeners[index]=null;
1254

    
1255
                return true;
1256

    
1257
            },
1258

    
1259
            /**
1260
             * Returns the event's target element.  Safari sometimes provides
1261
             * a text node, and this is automatically resolved to the text
1262
             * node's parent so that it behaves like other browsers.
1263
             * @method getTarget
1264
             * @param {Event} ev the event
1265
             * @param {boolean} resolveTextNode when set to true the target's
1266
             *                  parent will be returned if the target is a 
1267
             *                  text node.  @deprecated, the text node is
1268
             *                  now resolved automatically
1269
             * @return {HTMLElement} the event's target
1270
             * @static
1271
             */
1272
            getTarget: function(ev, resolveTextNode) {
1273
                var t = ev.target || ev.srcElement;
1274
                return this.resolveTextNode(t);
1275
            },
1276

    
1277
            /**
1278
             * In some cases, some browsers will return a text node inside
1279
             * the actual element that was targeted.  This normalizes the
1280
             * return value for getTarget and getRelatedTarget.
1281
             * @method resolveTextNode
1282
             * @param {HTMLElement} node node to resolve
1283
             * @return {HTMLElement} the normized node
1284
             * @static
1285
             */
1286
            resolveTextNode: function(n) {
1287
                try {
1288
                    if (n && 3 == n.nodeType) {
1289
                        return n.parentNode;
1290
                    }
1291
                } catch(e) { }
1292

    
1293
                return n;
1294
            },
1295

    
1296
            /**
1297
             * Returns the event's pageX
1298
             * @method getPageX
1299
             * @param {Event} ev the event
1300
             * @return {int} the event's pageX
1301
             * @static
1302
             */
1303
            getPageX: function(ev) {
1304
                var x = ev.pageX;
1305
                if (!x && 0 !== x) {
1306
                    x = ev.clientX || 0;
1307

    
1308
                    if ( this.isIE ) {
1309
                        x += this._getScrollLeft();
1310
                    }
1311
                }
1312

    
1313
                return x;
1314
            },
1315

    
1316
            /**
1317
             * Returns the event's pageY
1318
             * @method getPageY
1319
             * @param {Event} ev the event
1320
             * @return {int} the event's pageY
1321
             * @static
1322
             */
1323
            getPageY: function(ev) {
1324
                var y = ev.pageY;
1325
                if (!y && 0 !== y) {
1326
                    y = ev.clientY || 0;
1327

    
1328
                    if ( this.isIE ) {
1329
                        y += this._getScrollTop();
1330
                    }
1331
                }
1332

    
1333

    
1334
                return y;
1335
            },
1336

    
1337
            /**
1338
             * Returns the pageX and pageY properties as an indexed array.
1339
             * @method getXY
1340
             * @param {Event} ev the event
1341
             * @return {[x, y]} the pageX and pageY properties of the event
1342
             * @static
1343
             */
1344
            getXY: function(ev) {
1345
                return [this.getPageX(ev), this.getPageY(ev)];
1346
            },
1347

    
1348
            /**
1349
             * Returns the event's related target 
1350
             * @method getRelatedTarget
1351
             * @param {Event} ev the event
1352
             * @return {HTMLElement} the event's relatedTarget
1353
             * @static
1354
             */
1355
            getRelatedTarget: function(ev) {
1356
                var t = ev.relatedTarget;
1357
                if (!t) {
1358
                    if (ev.type == "mouseout") {
1359
                        t = ev.toElement;
1360
                    } else if (ev.type == "mouseover") {
1361
                        t = ev.fromElement;
1362
                    }
1363
                }
1364

    
1365
                return this.resolveTextNode(t);
1366
            },
1367

    
1368
            /**
1369
             * Returns the time of the event.  If the time is not included, the
1370
             * event is modified using the current time.
1371
             * @method getTime
1372
             * @param {Event} ev the event
1373
             * @return {Date} the time of the event
1374
             * @static
1375
             */
1376
            getTime: function(ev) {
1377
                if (!ev.time) {
1378
                    var t = new Date().getTime();
1379
                    try {
1380
                        ev.time = t;
1381
                    } catch(ex) { 
1382
                        this.lastError = ex;
1383
                        return t;
1384
                    }
1385
                }
1386

    
1387
                return ev.time;
1388
            },
1389

    
1390
            /**
1391
             * Convenience method for stopPropagation + preventDefault
1392
             * @method stopEvent
1393
             * @param {Event} ev the event
1394
             * @static
1395
             */
1396
            stopEvent: function(ev) {
1397
                this.stopPropagation(ev);
1398
                this.preventDefault(ev);
1399
            },
1400

    
1401
            /**
1402
             * Stops event propagation
1403
             * @method stopPropagation
1404
             * @param {Event} ev the event
1405
             * @static
1406
             */
1407
            stopPropagation: function(ev) {
1408
                if (ev.stopPropagation) {
1409
                    ev.stopPropagation();
1410
                } else {
1411
                    ev.cancelBubble = true;
1412
                }
1413
            },
1414

    
1415
            /**
1416
             * Prevents the default behavior of the event
1417
             * @method preventDefault
1418
             * @param {Event} ev the event
1419
             * @static
1420
             */
1421
            preventDefault: function(ev) {
1422
                if (ev.preventDefault) {
1423
                    ev.preventDefault();
1424
                } else {
1425
                    ev.returnValue = false;
1426
                }
1427
            },
1428
             
1429
            /**
1430
             * Finds the event in the window object, the caller's arguments, or
1431
             * in the arguments of another method in the callstack.  This is
1432
             * executed automatically for events registered through the event
1433
             * manager, so the implementer should not normally need to execute
1434
             * this function at all.
1435
             * @method getEvent
1436
             * @param {Event} e the event parameter from the handler
1437
             * @param {HTMLElement} boundEl the element the listener is attached to
1438
             * @return {Event} the event 
1439
             * @static
1440
             */
1441
            getEvent: function(e, boundEl) {
1442
                var ev = e || window.event;
1443

    
1444
                if (!ev) {
1445
                    var c = this.getEvent.caller;
1446
                    while (c) {
1447
                        ev = c.arguments[0];
1448
                        if (ev && Event == ev.constructor) {
1449
                            break;
1450
                        }
1451
                        c = c.caller;
1452
                    }
1453
                }
1454

    
1455
                return ev;
1456
            },
1457

    
1458
            /**
1459
             * Returns the charcode for an event
1460
             * @method getCharCode
1461
             * @param {Event} ev the event
1462
             * @return {int} the event's charCode
1463
             * @static
1464
             */
1465
            getCharCode: function(ev) {
1466
                var code = ev.keyCode || ev.charCode || 0;
1467

    
1468
                // webkit key normalization
1469
                if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
1470
                    code = webkitKeymap[code];
1471
                }
1472
                return code;
1473
            },
1474

    
1475
            /**
1476
             * Locating the saved event handler data by function ref
1477
             *
1478
             * @method _getCacheIndex
1479
             * @static
1480
             * @private
1481
             */
1482
            _getCacheIndex: function(a, el, sType, fn) {
1483
                for (var i=0, l=a.length; i<l; i=i+1) {
1484
                    var li = a[i];
1485
                    if ( li                 && 
1486
                         li[this.FN] == fn  && 
1487
                         li[this.EL] == el  && 
1488
                         li[this.TYPE] == sType ) {
1489
                        return i;
1490
                    }
1491
                }
1492

    
1493
                return -1;
1494
            },
1495

    
1496
            /**
1497
             * Generates an unique ID for the element if it does not already 
1498
             * have one.
1499
             * @method generateId
1500
             * @param el the element to create the id for
1501
             * @return {string} the resulting id of the element
1502
             * @static
1503
             */
1504
            generateId: function(el) {
1505
                var id = el.id;
1506

    
1507
                if (!id) {
1508
                    id = "yuievtautoid-" + counter;
1509
                    ++counter;
1510
                    el.id = id;
1511
                }
1512

    
1513
                return id;
1514
            },
1515

    
1516

    
1517
            /**
1518
             * We want to be able to use getElementsByTagName as a collection
1519
             * to attach a group of events to.  Unfortunately, different 
1520
             * browsers return different types of collections.  This function
1521
             * tests to determine if the object is array-like.  It will also 
1522
             * fail if the object is an array, but is empty.
1523
             * @method _isValidCollection
1524
             * @param o the object to test
1525
             * @return {boolean} true if the object is array-like and populated
1526
             * @static
1527
             * @private
1528
             */
1529
            _isValidCollection: function(o) {
1530
                try {
1531
                    return ( o                     && // o is something
1532
                             typeof o !== "string" && // o is not a string
1533
                             o.length              && // o is indexed
1534
                             !o.tagName            && // o is not an HTML element
1535
                             !o.alert              && // o is not a window
1536
                             typeof o[0] !== "undefined" );
1537
                } catch(ex) {
1538
                    YAHOO.log("node access error (xframe?)", "warn");
1539
                    return false;
1540
                }
1541

    
1542
            },
1543

    
1544
            /**
1545
             * @private
1546
             * @property elCache
1547
             * DOM element cache
1548
             * @static
1549
             * @deprecated Elements are not cached due to issues that arise when
1550
             * elements are removed and re-added
1551
             */
1552
            elCache: {},
1553

    
1554
            /**
1555
             * We cache elements bound by id because when the unload event 
1556
             * fires, we can no longer use document.getElementById
1557
             * @method getEl
1558
             * @static
1559
             * @private
1560
             * @deprecated Elements are not cached any longer
1561
             */
1562
            getEl: function(id) {
1563
                return (typeof id === "string") ? document.getElementById(id) : id;
1564
            },
1565

    
1566
            /**
1567
             * Clears the element cache
1568
             * @deprecated Elements are not cached any longer
1569
             * @method clearCache
1570
             * @static
1571
             * @private
1572
             */
1573
            clearCache: function() { },
1574

    
1575
            /**
1576
             * Custom event the fires when the dom is initially usable
1577
             * @event DOMReadyEvent
1578
             */
1579
            DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1),
1580

    
1581
            /**
1582
             * hook up any deferred listeners
1583
             * @method _load
1584
             * @static
1585
             * @private
1586
             */
1587
            _load: function(e) {
1588

    
1589
                if (!loadComplete) {
1590
                    loadComplete = true;
1591
                    var EU = YAHOO.util.Event;
1592

    
1593
                    // Just in case DOMReady did not go off for some reason
1594
                    EU._ready();
1595

    
1596
                    // Available elements may not have been detected before the
1597
                    // window load event fires. Try to find them now so that the
1598
                    // the user is more likely to get the onAvailable notifications
1599
                    // before the window load notification
1600
                    EU._tryPreloadAttach();
1601

    
1602
                }
1603
            },
1604

    
1605
            /**
1606
             * Fires the DOMReady event listeners the first time the document is
1607
             * usable.
1608
             * @method _ready
1609
             * @static
1610
             * @private
1611
             */
1612
            _ready: function(e) {
1613
                var EU = YAHOO.util.Event;
1614
                if (!EU.DOMReady) {
1615
                    EU.DOMReady=true;
1616

    
1617
                    // Fire the content ready custom event
1618
                    EU.DOMReadyEvent.fire();
1619

    
1620
                    // Remove the DOMContentLoaded (FF/Opera)
1621
                    EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
1622
                }
1623
            },
1624

    
1625
            /**
1626
             * Polling function that runs before the onload event fires, 
1627
             * attempting to attach to DOM Nodes as soon as they are 
1628
             * available
1629
             * @method _tryPreloadAttach
1630
             * @static
1631
             * @private
1632
             */
1633
            _tryPreloadAttach: function() {
1634

    
1635
                if (onAvailStack.length === 0) {
1636
                    retryCount = 0;
1637
                    if (this._interval) {
1638
                        // clearInterval(this._interval);
1639
                        this._interval.cancel();
1640
                        this._interval = null;
1641
                    } 
1642
                    return;
1643
                }
1644

    
1645
                if (this.locked) {
1646
                    return;
1647
                }
1648

    
1649
                if (this.isIE) {
1650
                    // Hold off if DOMReady has not fired and check current
1651
                    // readyState to protect against the IE operation aborted
1652
                    // issue.
1653
                    if (!this.DOMReady) {
1654
                        this.startInterval();
1655
                        return;
1656
                    }
1657
                }
1658

    
1659
                this.locked = true;
1660

    
1661
                // this.logger.debug("tryPreloadAttach");
1662

    
1663
                // keep trying until after the page is loaded.  We need to 
1664
                // check the page load state prior to trying to bind the 
1665
                // elements so that we can be certain all elements have been 
1666
                // tested appropriately
1667
                var tryAgain = !loadComplete;
1668
                if (!tryAgain) {
1669
                    tryAgain = (retryCount > 0 && onAvailStack.length > 0);
1670
                }
1671

    
1672
                // onAvailable
1673
                var notAvail = [];
1674

    
1675
                var executeItem = function (el, item) {
1676
                    var context = el;
1677
                    if (item.overrideContext) {
1678
                        if (item.overrideContext === true) {
1679
                            context = item.obj;
1680
                        } else {
1681
                            context = item.overrideContext;
1682
                        }
1683
                    }
1684
                    item.fn.call(context, item.obj);
1685
                };
1686

    
1687
                var i, len, item, el, ready=[];
1688

    
1689
                // onAvailable onContentReady
1690
                for (i=0, len=onAvailStack.length; i<len; i=i+1) {
1691
                    item = onAvailStack[i];
1692
                    if (item) {
1693
                        el = this.getEl(item.id);
1694
                        if (el) {
1695
                            if (item.checkReady) {
1696
                                if (loadComplete || el.nextSibling || !tryAgain) {
1697
                                    ready.push(item);
1698
                                    onAvailStack[i] = null;
1699
                                }
1700
                            } else {
1701
                                executeItem(el, item);
1702
                                onAvailStack[i] = null;
1703
                            }
1704
                        } else {
1705
                            notAvail.push(item);
1706
                        }
1707
                    }
1708
                }
1709
                
1710
                // make sure onContentReady fires after onAvailable
1711
                for (i=0, len=ready.length; i<len; i=i+1) {
1712
                    item = ready[i];
1713
                    executeItem(this.getEl(item.id), item);
1714
                }
1715

    
1716

    
1717
                retryCount--;
1718

    
1719
                if (tryAgain) {
1720
                    for (i=onAvailStack.length-1; i>-1; i--) {
1721
                        item = onAvailStack[i];
1722
                        if (!item || !item.id) {
1723
                            onAvailStack.splice(i, 1);
1724
                        }
1725
                    }
1726

    
1727
                    this.startInterval();
1728
                } else {
1729
                    if (this._interval) {
1730
                        // clearInterval(this._interval);
1731
                        this._interval.cancel();
1732
                        this._interval = null;
1733
                    }
1734
                }
1735

    
1736
                this.locked = false;
1737

    
1738
            },
1739

    
1740
            /**
1741
             * Removes all listeners attached to the given element via addListener.
1742
             * Optionally, the node's children can also be purged.
1743
             * Optionally, you can specify a specific type of event to remove.
1744
             * @method purgeElement
1745
             * @param {HTMLElement} el the element to purge
1746
             * @param {boolean} recurse recursively purge this element's children
1747
             * as well.  Use with caution.
1748
             * @param {string} sType optional type of listener to purge. If
1749
             * left out, all listeners will be removed
1750
             * @static
1751
             */
1752
            purgeElement: function(el, recurse, sType) {
1753
                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
1754
                var elListeners = this.getListeners(oEl, sType), i, len;
1755
                if (elListeners) {
1756
                    for (i=elListeners.length-1; i>-1; i--) {
1757
                        var l = elListeners[i];
1758
                        this.removeListener(oEl, l.type, l.fn);
1759
                    }
1760
                }
1761

    
1762
                if (recurse && oEl && oEl.childNodes) {
1763
                    for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
1764
                        this.purgeElement(oEl.childNodes[i], recurse, sType);
1765
                    }
1766
                }
1767
            },
1768

    
1769
            /**
1770
             * Returns all listeners attached to the given element via addListener.
1771
             * Optionally, you can specify a specific type of event to return.
1772
             * @method getListeners
1773
             * @param el {HTMLElement|string} the element or element id to inspect 
1774
             * @param sType {string} optional type of listener to return. If
1775
             * left out, all listeners will be returned
1776
             * @return {Object} the listener. Contains the following fields:
1777
             * &nbsp;&nbsp;type:   (string)   the type of event
1778
             * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
1779
             * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
1780
             * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default context
1781
             * &nbsp;&nbsp;scope: (boolean)  the derived context based on the adjust parameter
1782
             * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
1783
             * @static
1784
             */           
1785
            getListeners: function(el, sType) {
1786
                var results=[], searchLists;
1787
                if (!sType) {
1788
                    searchLists = [listeners, unloadListeners];
1789
                } else if (sType === "unload") {
1790
                    searchLists = [unloadListeners];
1791
                } else {
1792
					sType = this._getType(sType);
1793
                    searchLists = [listeners];
1794
                }
1795

    
1796
                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
1797

    
1798
                for (var j=0;j<searchLists.length; j=j+1) {
1799
                    var searchList = searchLists[j];
1800
                    if (searchList) {
1801
                        for (var i=0,len=searchList.length; i<len ; ++i) {
1802
                            var l = searchList[i];
1803
                            if ( l  && l[this.EL] === oEl && 
1804
                                    (!sType || sType === l[this.TYPE]) ) {
1805
                                results.push({
1806
                                    type:   l[this.TYPE],
1807
                                    fn:     l[this.FN],
1808
                                    obj:    l[this.OBJ],
1809
                                    adjust: l[this.OVERRIDE],
1810
                                    scope:  l[this.ADJ_SCOPE],
1811
                                    index:  i
1812
                                });
1813
                            }
1814
                        }
1815
                    }
1816
                }
1817

    
1818
                return (results.length) ? results : null;
1819
            },
1820

    
1821
            /**
1822
             * Removes all listeners registered by pe.event.  Called 
1823
             * automatically during the unload event.
1824
             * @method _unload
1825
             * @static
1826
             * @private
1827
             */
1828
            _unload: function(e) {
1829

    
1830
                var EU = YAHOO.util.Event, i, j, l, len, index,
1831
                         ul = unloadListeners.slice(), context;
1832

    
1833
                // execute and clear stored unload listeners
1834
                for (i=0, len=unloadListeners.length; i<len; ++i) {
1835
                    l = ul[i];
1836
                    if (l) {
1837
                        context = window;
1838
                        if (l[EU.ADJ_SCOPE]) {
1839
                            if (l[EU.ADJ_SCOPE] === true) {
1840
                                context = l[EU.UNLOAD_OBJ];
1841
                            } else {
1842
                                context = l[EU.ADJ_SCOPE];
1843
                            }
1844
                        }
1845
                        l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
1846
                        ul[i] = null;
1847
                    }
1848
                }
1849

    
1850
                l = null;
1851
                context = null;
1852
                unloadListeners = null;
1853

    
1854
                // Remove listeners to handle IE memory leaks
1855
                // 2.5.0 listeners are removed for all browsers again.  FireFox preserves
1856
                // at least some listeners between page refreshes, potentially causing
1857
                // errors during page load (mouseover listeners firing before they
1858
                // should if the user moves the mouse at the correct moment).
1859
                if (listeners) {
1860
                    for (j=listeners.length-1; j>-1; j--) {
1861
                        l = listeners[j];
1862
                        if (l) {
1863
                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j);
1864
                        } 
1865
                    }
1866
                    l=null;
1867
                }
1868

    
1869
                EU._simpleRemove(window, "unload", EU._unload);
1870

    
1871
            },
1872

    
1873
            /**
1874
             * Returns scrollLeft
1875
             * @method _getScrollLeft
1876
             * @static
1877
             * @private
1878
             */
1879
            _getScrollLeft: function() {
1880
                return this._getScroll()[1];
1881
            },
1882

    
1883
            /**
1884
             * Returns scrollTop
1885
             * @method _getScrollTop
1886
             * @static
1887
             * @private
1888
             */
1889
            _getScrollTop: function() {
1890
                return this._getScroll()[0];
1891
            },
1892

    
1893
            /**
1894
             * Returns the scrollTop and scrollLeft.  Used to calculate the 
1895
             * pageX and pageY in Internet Explorer
1896
             * @method _getScroll
1897
             * @static
1898
             * @private
1899
             */
1900
            _getScroll: function() {
1901
                var dd = document.documentElement, db = document.body;
1902
                if (dd && (dd.scrollTop || dd.scrollLeft)) {
1903
                    return [dd.scrollTop, dd.scrollLeft];
1904
                } else if (db) {
1905
                    return [db.scrollTop, db.scrollLeft];
1906
                } else {
1907
                    return [0, 0];
1908
                }
1909
            },
1910
            
1911
            /**
1912
             * Used by old versions of CustomEvent, restored for backwards
1913
             * compatibility
1914
             * @method regCE
1915
             * @private
1916
             * @static
1917
             * @deprecated still here for backwards compatibility
1918
             */
1919
            regCE: function() {},
1920

    
1921
            /**
1922
             * Adds a DOM event directly without the caching, cleanup, context adj, etc
1923
             *
1924
             * @method _simpleAdd
1925
             * @param {HTMLElement} el      the element to bind the handler to
1926
             * @param {string}      sType   the type of event handler
1927
             * @param {function}    fn      the callback to invoke
1928
             * @param {boolen}      capture capture or bubble phase
1929
             * @static
1930
             * @private
1931
             */
1932
            _simpleAdd: function () {
1933
                if (window.addEventListener) {
1934
                    return function(el, sType, fn, capture) {
1935
                        el.addEventListener(sType, fn, (capture));
1936
                    };
1937
                } else if (window.attachEvent) {
1938
                    return function(el, sType, fn, capture) {
1939
                        el.attachEvent("on" + sType, fn);
1940
                    };
1941
                } else {
1942
                    return function(){};
1943
                }
1944
            }(),
1945

    
1946
            /**
1947
             * Basic remove listener
1948
             *
1949
             * @method _simpleRemove
1950
             * @param {HTMLElement} el      the element to bind the handler to
1951
             * @param {string}      sType   the type of event handler
1952
             * @param {function}    fn      the callback to invoke
1953
             * @param {boolen}      capture capture or bubble phase
1954
             * @static
1955
             * @private
1956
             */
1957
            _simpleRemove: function() {
1958
                if (window.removeEventListener) {
1959
                    return function (el, sType, fn, capture) {
1960
                        el.removeEventListener(sType, fn, (capture));
1961
                    };
1962
                } else if (window.detachEvent) {
1963
                    return function (el, sType, fn) {
1964
                        el.detachEvent("on" + sType, fn);
1965
                    };
1966
                } else {
1967
                    return function(){};
1968
                }
1969
            }()
1970
        };
1971

    
1972
    }();
1973

    
1974
    (function() {
1975
        var EU = YAHOO.util.Event;
1976

    
1977
        /**
1978
         * YAHOO.util.Event.on is an alias for addListener
1979
         * @method on
1980
         * @see addListener
1981
         * @static
1982
         */
1983
        EU.on = EU.addListener;
1984

    
1985
        /**
1986
         * YAHOO.util.Event.onFocus is an alias for addFocusListener
1987
         * @method onFocus
1988
         * @see addFocusListener
1989
         * @static
1990
		 * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
1991
         */
1992
        EU.onFocus = EU.addFocusListener;
1993

    
1994
        /**
1995
         * YAHOO.util.Event.onBlur is an alias for addBlurListener
1996
         * @method onBlur
1997
         * @see addBlurListener
1998
         * @static
1999
		 * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
2000
         */     
2001
        EU.onBlur = EU.addBlurListener;
2002

    
2003
/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
2004

    
2005
        // Internet Explorer: use the readyState of a defered script.
2006
        // This isolates what appears to be a safe moment to manipulate
2007
        // the DOM prior to when the document's readyState suggests
2008
        // it is safe to do so.
2009
        if (EU.isIE) {
2010
            if (self !== self.top) {
2011
                document.onreadystatechange = function() {
2012
                    if (document.readyState == 'complete') {
2013
                        document.onreadystatechange = null;
2014
                        EU._ready();
2015
                    }
2016
                };
2017
            } else {
2018

    
2019
                // Process onAvailable/onContentReady items when the 
2020
                // DOM is ready.
2021
                YAHOO.util.Event.onDOMReady(
2022
                        YAHOO.util.Event._tryPreloadAttach,
2023
                        YAHOO.util.Event, true);
2024
                
2025
                var n = document.createElement('p');  
2026

    
2027
                EU._dri = setInterval(function() {
2028
                    try {
2029
                        // throws an error if doc is not ready
2030
                        n.doScroll('left');
2031
                        clearInterval(EU._dri);
2032
                        EU._dri = null;
2033
                        EU._ready();
2034
                        n = null;
2035
                    } catch (ex) { 
2036
                    }
2037
                }, EU.POLL_INTERVAL); 
2038
            }
2039

    
2040
        // The document's readyState in Safari currently will
2041
        // change to loaded/complete before images are loaded.
2042
        } else if (EU.webkit && EU.webkit < 525) {
2043

    
2044
            EU._dri = setInterval(function() {
2045
                var rs=document.readyState;
2046
                if ("loaded" == rs || "complete" == rs) {
2047
                    clearInterval(EU._dri);
2048
                    EU._dri = null;
2049
                    EU._ready();
2050
                }
2051
            }, EU.POLL_INTERVAL); 
2052

    
2053
        // FireFox and Opera: These browsers provide a event for this
2054
        // moment.  The latest WebKit releases now support this event.
2055
        } else {
2056

    
2057
            EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
2058

    
2059
        }
2060
        /////////////////////////////////////////////////////////////
2061

    
2062

    
2063
        EU._simpleAdd(window, "load", EU._load);
2064
        EU._simpleAdd(window, "unload", EU._unload);
2065
        EU._tryPreloadAttach();
2066
    })();
2067

    
2068
}
2069
/**
2070
 * EventProvider is designed to be used with YAHOO.augment to wrap 
2071
 * CustomEvents in an interface that allows events to be subscribed to 
2072
 * and fired by name.  This makes it possible for implementing code to
2073
 * subscribe to an event that either has not been created yet, or will
2074
 * not be created at all.
2075
 *
2076
 * @Class EventProvider
2077
 */
2078
YAHOO.util.EventProvider = function() { };
2079

    
2080
YAHOO.util.EventProvider.prototype = {
2081

    
2082
    /**
2083
     * Private storage of custom events
2084
     * @property __yui_events
2085
     * @type Object[]
2086
     * @private
2087
     */
2088
    __yui_events: null,
2089

    
2090
    /**
2091
     * Private storage of custom event subscribers
2092
     * @property __yui_subscribers
2093
     * @type Object[]
2094
     * @private
2095
     */
2096
    __yui_subscribers: null,
2097
    
2098
    /**
2099
     * Subscribe to a CustomEvent by event type
2100
     *
2101
     * @method subscribe
2102
     * @param p_type     {string}   the type, or name of the event
2103
     * @param p_fn       {function} the function to exectute when the event fires
2104
     * @param p_obj      {Object}   An object to be passed along when the event 
2105
     *                              fires
2106
     * @param overrideContext {boolean}  If true, the obj passed in becomes the 
2107
     *                              execution scope of the listener
2108
     */
2109
    subscribe: function(p_type, p_fn, p_obj, overrideContext) {
2110

    
2111
        this.__yui_events = this.__yui_events || {};
2112
        var ce = this.__yui_events[p_type];
2113

    
2114
        if (ce) {
2115
            ce.subscribe(p_fn, p_obj, overrideContext);
2116
        } else {
2117
            this.__yui_subscribers = this.__yui_subscribers || {};
2118
            var subs = this.__yui_subscribers;
2119
            if (!subs[p_type]) {
2120
                subs[p_type] = [];
2121
            }
2122
            subs[p_type].push(
2123
                { fn: p_fn, obj: p_obj, overrideContext: overrideContext } );
2124
        }
2125
    },
2126

    
2127
    /**
2128
     * Unsubscribes one or more listeners the from the specified event
2129
     * @method unsubscribe
2130
     * @param p_type {string}   The type, or name of the event.  If the type
2131
     *                          is not specified, it will attempt to remove
2132
     *                          the listener from all hosted events.
2133
     * @param p_fn   {Function} The subscribed function to unsubscribe, if not
2134
     *                          supplied, all subscribers will be removed.
2135
     * @param p_obj  {Object}   The custom object passed to subscribe.  This is
2136
     *                        optional, but if supplied will be used to
2137
     *                        disambiguate multiple listeners that are the same
2138
     *                        (e.g., you subscribe many object using a function
2139
     *                        that lives on the prototype)
2140
     * @return {boolean} true if the subscriber was found and detached.
2141
     */
2142
    unsubscribe: function(p_type, p_fn, p_obj) {
2143
        this.__yui_events = this.__yui_events || {};
2144
        var evts = this.__yui_events;
2145
        if (p_type) {
2146
            var ce = evts[p_type];
2147
            if (ce) {
2148
                return ce.unsubscribe(p_fn, p_obj);
2149
            }
2150
        } else {
2151
            var ret = true;
2152
            for (var i in evts) {
2153
                if (YAHOO.lang.hasOwnProperty(evts, i)) {
2154
                    ret = ret && evts[i].unsubscribe(p_fn, p_obj);
2155
                }
2156
            }
2157
            return ret;
2158
        }
2159

    
2160
        return false;
2161
    },
2162
    
2163
    /**
2164
     * Removes all listeners from the specified event.  If the event type
2165
     * is not specified, all listeners from all hosted custom events will
2166
     * be removed.
2167
     * @method unsubscribeAll
2168
     * @param p_type {string}   The type, or name of the event
2169
     */
2170
    unsubscribeAll: function(p_type) {
2171
        return this.unsubscribe(p_type);
2172
    },
2173

    
2174
    /**
2175
     * Creates a new custom event of the specified type.  If a custom event
2176
     * by that name already exists, it will not be re-created.  In either
2177
     * case the custom event is returned. 
2178
     *
2179
     * @method createEvent
2180
     *
2181
     * @param p_type {string} the type, or name of the event
2182
     * @param p_config {object} optional config params.  Valid properties are:
2183
     *
2184
     *  <ul>
2185
     *    <li>
2186
     *      scope: defines the default execution scope.  If not defined
2187
     *      the default scope will be this instance.
2188
     *    </li>
2189
     *    <li>
2190
     *      silent: if true, the custom event will not generate log messages.
2191
     *      This is false by default.
2192
     *    </li>
2193
     *    <li>
2194
     *      fireOnce: if true, the custom event will only notify subscribers
2195
     *      once regardless of the number of times the event is fired.  In
2196
     *      addition, new subscribers will be executed immediately if the
2197
     *      event has already fired.
2198
     *      This is false by default.
2199
     *    </li>
2200
     *    <li>
2201
     *      onSubscribeCallback: specifies a callback to execute when the
2202
     *      event has a new subscriber.  This will fire immediately for
2203
     *      each queued subscriber if any exist prior to the creation of
2204
     *      the event.
2205
     *    </li>
2206
     *  </ul>
2207
     *
2208
     *  @return {CustomEvent} the custom event
2209
     *
2210
     */
2211
    createEvent: function(p_type, p_config) {
2212

    
2213
        this.__yui_events = this.__yui_events || {};
2214
        var opts = p_config || {},
2215
            events = this.__yui_events, ce;
2216

    
2217
        if (events[p_type]) {
2218
YAHOO.log("EventProvider createEvent skipped: '"+p_type+"' already exists");
2219
        } else {
2220

    
2221
            ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent,
2222
                         YAHOO.util.CustomEvent.FLAT, opts.fireOnce);
2223

    
2224
            events[p_type] = ce;
2225

    
2226
            if (opts.onSubscribeCallback) {
2227
                ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
2228
            }
2229

    
2230
            this.__yui_subscribers = this.__yui_subscribers || {};
2231
            var qs = this.__yui_subscribers[p_type];
2232

    
2233
            if (qs) {
2234
                for (var i=0; i<qs.length; ++i) {
2235
                    ce.subscribe(qs[i].fn, qs[i].obj, qs[i].overrideContext);
2236
                }
2237
            }
2238
        }
2239

    
2240
        return events[p_type];
2241
    },
2242

    
2243

    
2244
   /**
2245
     * Fire a custom event by name.  The callback functions will be executed
2246
     * from the scope specified when the event was created, and with the 
2247
     * following parameters:
2248
     *   <ul>
2249
     *   <li>The first argument fire() was executed with</li>
2250
     *   <li>The custom object (if any) that was passed into the subscribe() 
2251
     *       method</li>
2252
     *   </ul>
2253
     * @method fireEvent
2254
     * @param p_type    {string}  the type, or name of the event
2255
     * @param arguments {Object*} an arbitrary set of parameters to pass to 
2256
     *                            the handler.
2257
     * @return {boolean} the return value from CustomEvent.fire
2258
     *                   
2259
     */
2260
    fireEvent: function(p_type) {
2261

    
2262
        this.__yui_events = this.__yui_events || {};
2263
        var ce = this.__yui_events[p_type];
2264

    
2265
        if (!ce) {
2266
YAHOO.log(p_type + "event fired before it was created.");
2267
            return null;
2268
        }
2269

    
2270
        var args = [];
2271
        for (var i=1; i<arguments.length; ++i) {
2272
            args.push(arguments[i]);
2273
        }
2274
        return ce.fire.apply(ce, args);
2275
    },
2276

    
2277
    /**
2278
     * Returns true if the custom event of the provided type has been created
2279
     * with createEvent.
2280
     * @method hasEvent
2281
     * @param type {string} the type, or name of the event
2282
     */
2283
    hasEvent: function(type) {
2284
        if (this.__yui_events) {
2285
            if (this.__yui_events[type]) {
2286
                return true;
2287
            }
2288
        }
2289
        return false;
2290
    }
2291

    
2292
};
2293

    
2294
(function() {
2295

    
2296
    var Event = YAHOO.util.Event, Lang = YAHOO.lang;
2297

    
2298
/**
2299
* KeyListener is a utility that provides an easy interface for listening for
2300
* keydown/keyup events fired against DOM elements.
2301
* @namespace YAHOO.util
2302
* @class KeyListener
2303
* @constructor
2304
* @param {HTMLElement} attachTo The element or element ID to which the key 
2305
*                               event should be attached
2306
* @param {String}      attachTo The element or element ID to which the key
2307
*                               event should be attached
2308
* @param {Object}      keyData  The object literal representing the key(s) 
2309
*                               to detect. Possible attributes are 
2310
*                               shift(boolean), alt(boolean), ctrl(boolean) 
2311
*                               and keys(either an int or an array of ints 
2312
*                               representing keycodes).
2313
* @param {Function}    handler  The CustomEvent handler to fire when the 
2314
*                               key event is detected
2315
* @param {Object}      handler  An object literal representing the handler. 
2316
* @param {String}      event    Optional. The event (keydown or keyup) to 
2317
*                               listen for. Defaults automatically to keydown.
2318
*
2319
* @knownissue the "keypress" event is completely broken in Safari 2.x and below.
2320
*             the workaround is use "keydown" for key listening.  However, if
2321
*             it is desired to prevent the default behavior of the keystroke,
2322
*             that can only be done on the keypress event.  This makes key
2323
*             handling quite ugly.
2324
* @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
2325
*             There currently is no workaround other than choosing another
2326
*             key to listen for.
2327
*/
2328
YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
2329
    if (!attachTo) {
2330
        YAHOO.log("No attachTo element specified", "error");
2331
    } else if (!keyData) {
2332
        YAHOO.log("No keyData specified", "error");
2333
    } else if (!handler) {
2334
        YAHOO.log("No handler specified", "error");
2335
    } 
2336
    
2337
    if (!event) {
2338
        event = YAHOO.util.KeyListener.KEYDOWN;
2339
    }
2340

    
2341
    /**
2342
    * The CustomEvent fired internally when a key is pressed
2343
    * @event keyEvent
2344
    * @private
2345
    * @param {Object} keyData The object literal representing the key(s) to 
2346
    *                         detect. Possible attributes are shift(boolean), 
2347
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2348
    *                         int or an array of ints representing keycodes).
2349
    */
2350
    var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
2351
    
2352
    /**
2353
    * The CustomEvent fired when the KeyListener is enabled via the enable() 
2354
    * function
2355
    * @event enabledEvent
2356
    * @param {Object} keyData The object literal representing the key(s) to 
2357
    *                         detect. Possible attributes are shift(boolean), 
2358
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2359
    *                         int or an array of ints representing keycodes).
2360
    */
2361
    this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
2362

    
2363
    /**
2364
    * The CustomEvent fired when the KeyListener is disabled via the 
2365
    * disable() function
2366
    * @event disabledEvent
2367
    * @param {Object} keyData The object literal representing the key(s) to 
2368
    *                         detect. Possible attributes are shift(boolean), 
2369
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2370
    *                         int or an array of ints representing keycodes).
2371
    */
2372
    this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
2373

    
2374
    if (Lang.isString(attachTo)) {
2375
        attachTo = document.getElementById(attachTo); // No Dom util
2376
    }
2377

    
2378
    if (Lang.isFunction(handler)) {
2379
        keyEvent.subscribe(handler);
2380
    } else {
2381
        keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
2382
    }
2383

    
2384
    /**
2385
    * Handles the key event when a key is pressed.
2386
    * @method handleKeyPress
2387
    * @param {DOMEvent} e   The keypress DOM event
2388
    * @param {Object}   obj The DOM event scope object
2389
    * @private
2390
    */
2391
    function handleKeyPress(e, obj) {
2392
        if (! keyData.shift) {  
2393
            keyData.shift = false; 
2394
        }
2395
        if (! keyData.alt) {    
2396
            keyData.alt = false;
2397
        }
2398
        if (! keyData.ctrl) {
2399
            keyData.ctrl = false;
2400
        }
2401

    
2402
        // check held down modifying keys first
2403
        if (e.shiftKey == keyData.shift && 
2404
            e.altKey   == keyData.alt &&
2405
            e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
2406
            
2407
            var dataItem, keys = keyData.keys, key;
2408

    
2409
            if (YAHOO.lang.isArray(keys)) {
2410
                for (var i=0;i<keys.length;i++) {
2411
                    dataItem = keys[i];
2412
                    key = Event.getCharCode(e);
2413

    
2414
                    if (dataItem == key) {
2415
                        keyEvent.fire(key, e);
2416
                        break;
2417
                    }
2418
                }
2419
            } else {
2420
                key = Event.getCharCode(e);
2421
                if (keys == key ) {
2422
                    keyEvent.fire(key, e);
2423
                }
2424
            }
2425
        }
2426
    }
2427

    
2428
    /**
2429
    * Enables the KeyListener by attaching the DOM event listeners to the 
2430
    * target DOM element
2431
    * @method enable
2432
    */
2433
    this.enable = function() {
2434
        if (! this.enabled) {
2435
            Event.on(attachTo, event, handleKeyPress);
2436
            this.enabledEvent.fire(keyData);
2437
        }
2438
        /**
2439
        * Boolean indicating the enabled/disabled state of the Tooltip
2440
        * @property enabled
2441
        * @type Boolean
2442
        */
2443
        this.enabled = true;
2444
    };
2445

    
2446
    /**
2447
    * Disables the KeyListener by removing the DOM event listeners from the 
2448
    * target DOM element
2449
    * @method disable
2450
    */
2451
    this.disable = function() {
2452
        if (this.enabled) {
2453
            Event.removeListener(attachTo, event, handleKeyPress);
2454
            this.disabledEvent.fire(keyData);
2455
        }
2456
        this.enabled = false;
2457
    };
2458

    
2459
    /**
2460
    * Returns a String representation of the object.
2461
    * @method toString
2462
    * @return {String}  The string representation of the KeyListener
2463
    */ 
2464
    this.toString = function() {
2465
        return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
2466
                (attachTo.id ? "[" + attachTo.id + "]" : "");
2467
    };
2468

    
2469
};
2470

    
2471
var KeyListener = YAHOO.util.KeyListener;
2472

    
2473
/**
2474
 * Constant representing the DOM "keydown" event.
2475
 * @property YAHOO.util.KeyListener.KEYDOWN
2476
 * @static
2477
 * @final
2478
 * @type String
2479
 */
2480
KeyListener.KEYDOWN = "keydown";
2481

    
2482
/**
2483
 * Constant representing the DOM "keyup" event.
2484
 * @property YAHOO.util.KeyListener.KEYUP
2485
 * @static
2486
 * @final
2487
 * @type String
2488
 */
2489
KeyListener.KEYUP = "keyup";
2490

    
2491
/**
2492
 * keycode constants for a subset of the special keys
2493
 * @property KEY
2494
 * @static
2495
 * @final
2496
 */
2497
KeyListener.KEY = {
2498
    ALT          : 18,
2499
    BACK_SPACE   : 8,
2500
    CAPS_LOCK    : 20,
2501
    CONTROL      : 17,
2502
    DELETE       : 46,
2503
    DOWN         : 40,
2504
    END          : 35,
2505
    ENTER        : 13,
2506
    ESCAPE       : 27,
2507
    HOME         : 36,
2508
    LEFT         : 37,
2509
    META         : 224,
2510
    NUM_LOCK     : 144,
2511
    PAGE_DOWN    : 34,
2512
    PAGE_UP      : 33, 
2513
    PAUSE        : 19,
2514
    PRINTSCREEN  : 44,
2515
    RIGHT        : 39,
2516
    SCROLL_LOCK  : 145,
2517
    SHIFT        : 16,
2518
    SPACE        : 32,
2519
    TAB          : 9,
2520
    UP           : 38
2521
};
2522

    
2523
})();
2524
YAHOO.register("event", YAHOO.util.Event, {version: "2.8.0r4", build: "2449"});
(2-2/5)