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
    }
115

    
116
    var onsubscribeType = "_YUICEOnSubscribe";
117

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

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

    
140
    } 
141

    
142

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

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

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

    
173
YAHOO.util.CustomEvent.prototype = {
174

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

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

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

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

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

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

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

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

    
229
        return found;
230
    },
231

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

    
250
        this.lastError = null;
251

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

    
255

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

    
258
        if (this.fireOnce) {
259
            if (this.fired) {
260
                return true;
261
            } else {
262
                this.firedWith = args;
263
            }
264
        }
265

    
266
        this.fired = true;
267

    
268
        if (!len && this.silent) {
269
            return true;
270
        }
271

    
272
        if (!this.silent) {
273
        }
274

    
275
        // make a copy of the subscribers so that there are
276
        // no index problems if one subscriber removes another.
277
        var subs = this.subscribers.slice();
278

    
279
        for (i=0; i<len; ++i) {
280
            var s = subs[i];
281
            if (!s) {
282
                rebuild=true;
283
            } else {
284

    
285
                ret = this.notify(s, args);
286

    
287
                if (false === ret) {
288
                    if (!this.silent) {
289
                    }
290

    
291
                    break;
292
                }
293
            }
294
        }
295

    
296
        return (ret !== false);
297
    },
298

    
299
    notify: function(s, args) {
300

    
301
        var ret, param=null, scope = s.getScope(this.scope),
302
                 throwErrors = YAHOO.util.Event.throwErrors;
303

    
304
        if (!this.silent) {
305
        }
306

    
307
        if (this.signature == YAHOO.util.CustomEvent.FLAT) {
308

    
309
            if (args.length > 0) {
310
                param = args[0];
311
            }
312

    
313
            try {
314
                ret = s.fn.call(scope, param, s.obj);
315
            } catch(e) {
316
                this.lastError = e;
317
                // errors.push(e);
318
                if (throwErrors) {
319
                    throw e;
320
                }
321
            }
322
        } else {
323
            try {
324
                ret = s.fn.call(scope, this.type, args, s.obj);
325
            } catch(ex) {
326
                this.lastError = ex;
327
                if (throwErrors) {
328
                    throw ex;
329
                }
330
            }
331
        }
332

    
333
        return ret;
334
    },
335

    
336
    /**
337
     * Removes all listeners
338
    * @method unsubscribeAll
339
    * @return {int} The number of listeners unsubscribed
340
     */
341
    unsubscribeAll: function() {
342
        var l = this.subscribers.length, i;
343
        for (i=l-1; i>-1; i--) {
344
            this._delete(i);
345
        }
346

    
347
        this.subscribers=[];
348

    
349
        return l;
350
    },
351

    
352
    /**
353
    * @method _delete
354
    * @private
355
     */
356
    _delete: function(index) {
357
        var s = this.subscribers[index];
358
        if (s) {
359
            delete s.fn;
360
            delete s.obj;
361
        }
362

    
363
        // this.subscribers[index]=null;
364
        this.subscribers.splice(index, 1);
365
    },
366

    
367
    /**
368
    * @method toString
369
     */
370
    toString: function() {
371
         return "CustomEvent: " + "'" + this.type  + "', " + 
372
             "context: " + this.scope;
373

    
374
    }
375
};
376

    
377
/////////////////////////////////////////////////////////////////////
378

    
379
/**
380
 * Stores the subscriber information to be used when the event fires.
381
 * @param {Function} fn       The function to execute
382
 * @param {Object}   obj      An object to be passed along when the event fires
383
 * @param {boolean}  overrideContext If true, the obj passed in becomes the execution
384
 *                            context of the listener
385
 * @class Subscriber
386
 * @constructor
387
 */
388
YAHOO.util.Subscriber = function(fn, obj, overrideContext) {
389

    
390
    /**
391
     * The callback that will be execute when the event fires
392
    * @property fn
393
    * @type function
394
     */
395
    this.fn = fn;
396

    
397
    /**
398
     * An optional custom object that will passed to the callback when
399
     * the event fires
400
    * @property obj
401
    * @type object
402
     */
403
    this.obj = YAHOO.lang.isUndefined(obj) ? null : obj;
404

    
405
    /**
406
     * The default execution context for the event listener is defined when the
407
     * event is created (usually the object which contains the event).
408
     * By setting overrideContext to true, the execution context becomes the custom
409
     * object passed in by the subscriber.  If overrideContext is an object, that 
410
     * object becomes the context.
411
    * @property overrideContext
412
    * @type boolean|object
413
     */
414
    this.overrideContext = overrideContext;
415

    
416
};
417

    
418
/**
419
 * Returns the execution context for this listener.  If overrideContext was set to true
420
 * the custom obj will be the context.  If overrideContext is an object, that is the
421
 * context, otherwise the default context will be used.
422
 * @method getScope
423
 * @param {Object} defaultScope the context to use if this listener does not
424
 *                              override it.
425
 */
426
YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
427
    if (this.overrideContext) {
428
        if (this.overrideContext === true) {
429
            return this.obj;
430
        } else {
431
            return this.overrideContext;
432
        }
433
    }
434
    return defaultScope;
435
};
436

    
437
/**
438
 * Returns true if the fn and obj match this objects properties.
439
 * Used by the unsubscribe method to match the right subscriber.
440
 *
441
 * @method contains
442
 * @param {Function} fn the function to execute
443
 * @param {Object} obj an object to be passed along when the event fires
444
 * @return {boolean} true if the supplied arguments match this 
445
 *                   subscriber's signature.
446
 */
447
YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
448
    if (obj) {
449
        return (this.fn == fn && this.obj == obj);
450
    } else {
451
        return (this.fn == fn);
452
    }
453
};
454

    
455
/**
456
 * @method toString
457
 */
458
YAHOO.util.Subscriber.prototype.toString = function() {
459
    return "Subscriber { obj: " + this.obj  + 
460
           ", overrideContext: " +  (this.overrideContext || "no") + " }";
461
};
462

    
463
/**
464
 * The Event Utility provides utilities for managing DOM Events and tools
465
 * for building event systems
466
 *
467
 * @module event
468
 * @title Event Utility
469
 * @namespace YAHOO.util
470
 * @requires yahoo
471
 */
472

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

    
479
/**
480
 * The event utility provides functions to add and remove event listeners,
481
 * event cleansing.  It also tries to automatically remove listeners it
482
 * registers during the unload event.
483
 *
484
 * @class Event
485
 * @static
486
 */
487
    YAHOO.util.Event = function() {
488

    
489
        /**
490
         * True after the onload event has fired
491
        * @property loadComplete
492
        * @type boolean
493
        * @static
494
        * @private
495
         */
496
        var loadComplete =  false,
497

    
498
        /**
499
         * Cache of wrapped listeners
500
        * @property listeners
501
        * @type array
502
        * @static
503
        * @private
504
         */
505
        listeners = [],
506

    
507

    
508
        /**
509
         * User-defined unload function that will be fired before all events
510
         * are detached
511
        * @property unloadListeners
512
        * @type array
513
        * @static
514
        * @private
515
         */
516
        unloadListeners = [],
517

    
518
        /**
519
         * The number of times to poll after window.onload.  This number is
520
         * increased if additional late-bound handlers are requested after
521
         * the page load.
522
        * @property retryCount
523
        * @static
524
        * @private
525
         */
526
        retryCount = 0,
527

    
528
        /**
529
         * onAvailable listeners
530
        * @property onAvailStack
531
        * @static
532
        * @private
533
         */
534
        onAvailStack = [],
535

    
536
        /**
537
         * Counter for auto id generation
538
        * @property counter
539
        * @static
540
        * @private
541
         */
542
        counter = 0,
543
        
544
        /**
545
         * Normalized keycodes for webkit/safari
546
        * @property webkitKeymap
547
        * @type {int: int}
548
        * @private
549
        * @static
550
        * @final
551
         */
552
         webkitKeymap = {
553
            63232: 38, // up
554
            63233: 40, // down
555
            63234: 37, // left
556
            63235: 39, // right
557
            63276: 33, // page up
558
            63277: 34, // page down
559
            25: 9      // SHIFT-TAB (Safari provides a different key code in
560
                       // this case, even though the shiftKey modifier is set)
561
        },
562

    
563
        isIE = YAHOO.env.ua.ie,
564

    
565
        // String constants used by the addFocusListener and removeFocusListener methods
566
        
567
           FOCUSIN = "focusin",
568
           FOCUSOUT = "focusout";
569

    
570
        return {
571

    
572
            /**
573
             * The number of times we should look for elements that are not
574
             * in the DOM at the time the event is requested after the document
575
             * has been loaded.  The default is 500@amp;40 ms, so it will poll
576
             * for 20 seconds or until all outstanding handlers are bound
577
             * (whichever comes first).
578
            * @property POLL_RETRYS
579
            * @type int
580
            * @static
581
            * @final
582
             */
583
            POLL_RETRYS: 500,
584

    
585
            /**
586
             * The poll interval in milliseconds
587
            * @property POLL_INTERVAL
588
            * @type int
589
            * @static
590
            * @final
591
             */
592
            POLL_INTERVAL: 40,
593

    
594
            /**
595
             * Element to bind, int constant
596
            * @property EL
597
            * @type int
598
            * @static
599
            * @final
600
             */
601
            EL: 0,
602

    
603
            /**
604
             * Type of event, int constant
605
            * @property TYPE
606
            * @type int
607
            * @static
608
            * @final
609
             */
610
            TYPE: 1,
611

    
612
            /**
613
             * Function to execute, int constant
614
            * @property FN
615
            * @type int
616
            * @static
617
            * @final
618
             */
619
            FN: 2,
620

    
621
            /**
622
             * Function wrapped for context correction and cleanup, int constant
623
            * @property WFN
624
            * @type int
625
            * @static
626
            * @final
627
             */
628
            WFN: 3,
629

    
630
            /**
631
             * Object passed in by the user that will be returned as a 
632
             * parameter to the callback, int constant.  Specific to
633
             * unload listeners
634
            * @property OBJ
635
            * @type int
636
            * @static
637
            * @final
638
             */
639
            UNLOAD_OBJ: 3,
640

    
641
            /**
642
             * Adjusted context, either the element we are registering the event
643
             * on or the custom object passed in by the listener, int constant
644
            * @property ADJ_SCOPE
645
            * @type int
646
            * @static
647
            * @final
648
             */
649
            ADJ_SCOPE: 4,
650

    
651
            /**
652
             * The original obj passed into addListener
653
            * @property OBJ
654
            * @type int
655
            * @static
656
            * @final
657
             */
658
            OBJ: 5,
659

    
660
            /**
661
             * The original context parameter passed into addListener
662
            * @property OVERRIDE
663
            * @type int
664
            * @static
665
            * @final
666
             */
667
            OVERRIDE: 6,
668

    
669
            /**
670
             * The original capture parameter passed into addListener
671
            * @property CAPTURE
672
            * @type int
673
            * @static
674
            * @final
675
             */
676
            CAPTURE: 7,
677

    
678
            /**
679
             * addListener/removeListener can throw errors in unexpected scenarios.
680
             * These errors are suppressed, the method returns false, and this property
681
             * is set
682
            * @property lastError
683
            * @static
684
            * @type Error
685
             */
686
            lastError: null,
687

    
688
            /**
689
             * Safari detection
690
            * @property isSafari
691
            * @private
692
            * @static
693
            * @deprecated use YAHOO.env.ua.webkit
694
             */
695
            isSafari: YAHOO.env.ua.webkit,
696
            
697
            /**
698
             * webkit version
699
            * @property webkit
700
            * @type string
701
            * @private
702
            * @static
703
            * @deprecated use YAHOO.env.ua.webkit
704
             */
705
            webkit: YAHOO.env.ua.webkit,
706
            
707
            /**
708
             * IE detection 
709
            * @property isIE
710
            * @private
711
            * @static
712
            * @deprecated use YAHOO.env.ua.ie
713
             */
714
            isIE: isIE,
715

    
716
            /**
717
             * poll handle
718
            * @property _interval
719
            * @static
720
            * @private
721
             */
722
            _interval: null,
723

    
724
            /**
725
             * document readystate poll handle
726
            * @property _dri
727
            * @static
728
            * @private
729
             */
730
             _dri: null,
731

    
732

    
733
            /**
734
             * Map of special event types
735
            * @property _specialTypes
736
            * @static
737
            * @private
738
             */
739
            _specialTypes: {
740
                focusin: (isIE ? "focusin" : "focus"),
741
                focusout: (isIE ? "focusout" : "blur")
742
            },
743

    
744

    
745
            /**
746
             * True when the document is initially usable
747
            * @property DOMReady
748
            * @type boolean
749
            * @static
750
             */
751
            DOMReady: false,
752

    
753
            /**
754
             * Errors thrown by subscribers of custom events are caught
755
             * and the error message is written to the debug console.  If
756
             * this property is set to true, it will also re-throw the
757
             * error.
758
            * @property throwErrors
759
            * @type boolean
760
            * @default false
761
             */
762
            throwErrors: false,
763

    
764

    
765
            /**
766
            * @method startInterval
767
            * @static
768
            * @private
769
             */
770
            startInterval: function() {
771
                if (!this._interval) {
772
                    // var self = this;
773
                    // var callback = function() { self._tryPreloadAttach(); };
774
                    // this._interval = setInterval(callback, this.POLL_INTERVAL);
775
                    this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true);
776
                }
777
            },
778

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

    
805
                var a = (YAHOO.lang.isString(id)) ? [id] : id;
806

    
807
                for (var i=0; i<a.length; i=i+1) {
808
                    onAvailStack.push({id:         a[i], 
809
                                       fn:         fn, 
810
                                       obj:        obj, 
811
                                       overrideContext:   overrideContext, 
812
                                       checkReady: checkContent });
813
                }
814

    
815
                retryCount = this.POLL_RETRYS;
816

    
817
                this.startInterval();
818
            },
819

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

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

    
881

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

    
908
                if (!fn || !fn.call) {
909
                    return false;
910
                }
911

    
912
                // The el argument can be an array of elements or element ids.
913
                if ( this._isValidCollection(el)) {
914
                    var ok = true;
915
                    for (var i=0,len=el.length; i<len; ++i) {
916
                        ok = this.on(el[i], 
917
                                       sType, 
918
                                       fn, 
919
                                       obj, 
920
                                       overrideContext) && ok;
921
                    }
922
                    return ok;
923

    
924
                } else if (YAHOO.lang.isString(el)) {
925
                    var oEl = this.getEl(el);
926
                    // If the el argument is a string, we assume it is 
927
                    // actually the id of the element.  If the page is loaded
928
                    // we convert el to the actual element, otherwise we 
929
                    // defer attaching the event until onload event fires
930

    
931
                    // check to see if we need to delay hooking up the event 
932
                    // until after the page loads.
933
                    if (oEl) {
934
                        el = oEl;
935
                    } else {
936
                        // defer adding the event until the element is available
937
                        this.onAvailable(el, function() {
938
                           YAHOO.util.Event._addListener(el, sType, fn, obj, overrideContext, bCapture);
939
                        });
940

    
941
                        return true;
942
                    }
943
                }
944

    
945
                // Element should be an html element or an array if we get 
946
                // here.
947
                if (!el) {
948
                    return false;
949
                }
950

    
951
                // we need to make sure we fire registered unload events 
952
                // prior to automatically unhooking them.  So we hang on to 
953
                // these instead of attaching them to the window and fire the
954
                // handles explicitly during our one unload event.
955
                if ("unload" == sType && obj !== this) {
956
                    unloadListeners[unloadListeners.length] =
957
                            [el, sType, fn, obj, overrideContext];
958
                    return true;
959
                }
960

    
961

    
962
                // if the user chooses to override the context, we use the custom
963
                // object passed in, otherwise the executing context will be the
964
                // HTML element that the event is registered on
965
                var context = el;
966
                if (overrideContext) {
967
                    if (overrideContext === true) {
968
                        context = obj;
969
                    } else {
970
                        context = overrideContext;
971
                    }
972
                }
973

    
974
                // wrap the function so we can return the obj object when
975
                // the event fires;
976
                var wrappedFn = function(e) {
977
                        return fn.call(context, YAHOO.util.Event.getEvent(e, el), 
978
                                obj);
979
                    };
980

    
981
                var li = [el, sType, fn, wrappedFn, context, obj, overrideContext, bCapture];
982
                var index = listeners.length;
983
                // cache the listener so we can try to automatically unload
984
                listeners[index] = li;
985

    
986
                try {
987
                    this._simpleAdd(el, sType, wrappedFn, bCapture);
988
                } catch(ex) {
989
                    // handle an error trying to attach an event.  If it fails
990
                    // we need to clean up the cache
991
                    this.lastError = ex;
992
                    this.removeListener(el, sType, fn);
993
                    return false;
994
                }
995

    
996
                return true;
997
                
998
            },
999

    
1000
            /**
1001
             * Checks to see if the type requested is a special type 
1002
             * (as defined by the _specialTypes hash), and (if so) returns 
1003
             * the special type name.
1004
             *
1005
            * @method _getType
1006
             *
1007
            * @param {String}   sType     The type to look up
1008
            * @private
1009
             */
1010
            _getType: function (type) {
1011
            
1012
                return this._specialTypes[type] || type;
1013
                
1014
            },
1015

    
1016

    
1017
            /**
1018
             * Appends an event handler
1019
             *
1020
            * @method addListener
1021
             *
1022
            * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1023
             *  reference, or a collection of ids and/or elements to assign the 
1024
             *  listener to.
1025
            * @param {String}   sType     The type of event to append
1026
            * @param {Function} fn        The method the event invokes
1027
            * @param {Object}   obj    An arbitrary object that will be 
1028
             *                             passed as a parameter to the handler
1029
            * @param {Boolean|object}  overrideContext  If true, the obj passed in becomes
1030
             *                             the execution context of the listener. If an
1031
             *                             object, this object becomes the execution
1032
             *                             context.
1033
            * @return {Boolean} True if the action was successful or defered,
1034
             *                        false if one or more of the elements 
1035
             *                        could not have the listener attached,
1036
             *                        or if the operation throws an exception.
1037
            * @static
1038
             */
1039
            addListener: function (el, sType, fn, obj, overrideContext) {
1040

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

    
1043
                return this._addListener(el, this._getType(sType), fn, obj, overrideContext, capture);
1044

    
1045
            },
1046

    
1047

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

    
1075

    
1076
            /**
1077
             * Removes a focusin event listener to the specified element for 
1078
             * the purpose of listening for the focus event on the element's 
1079
             * descendants.
1080
             *
1081
            * @method removeFocusListener
1082
             *
1083
            * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1084
             *  reference, or a collection of ids and/or elements to remove
1085
             *  the listener from.
1086
            * @param {Function} fn the method the event invokes.  If fn is
1087
             *  undefined, then all event handlers for the type of event are 
1088
             *  removed.
1089
            * @return {boolean} true if the unbind was successful, false 
1090
             *  otherwise.
1091
            * @static
1092
              * @deprecated use YAHOO.util.Event.removeListener and specify "focusin" as the event type.
1093
             */
1094
            removeFocusListener: function (el, fn) { 
1095
                return this.removeListener(el, FOCUSIN, fn);
1096
            },
1097

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

    
1126
            /**
1127
             * Removes a focusout event listener to the specified element for 
1128
             * the purpose of listening for the blur event on the element's 
1129
             * descendants.
1130
             *
1131
            * @method removeBlurListener
1132
             *
1133
            * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1134
             *  reference, or a collection of ids and/or elements to remove
1135
             *  the listener from.
1136
            * @param {Function} fn the method the event invokes.  If fn is
1137
             *  undefined, then all event handlers for the type of event are 
1138
             *  removed.
1139
            * @return {boolean} true if the unbind was successful, false 
1140
             *  otherwise.
1141
            * @static
1142
              * @deprecated use YAHOO.util.Event.removeListener and specify "focusout" as the event type.
1143
             */
1144
            removeBlurListener: function (el, fn) { 
1145
                return this.removeListener(el, FOCUSOUT, fn);
1146
            },
1147

    
1148
            /**
1149
             * Removes an event listener
1150
             *
1151
            * @method removeListener
1152
             *
1153
            * @param {String|HTMLElement|Array|NodeList} el An id, an element 
1154
             *  reference, or a collection of ids and/or elements to remove
1155
             *  the listener from.
1156
            * @param {String} sType the type of event to remove.
1157
            * @param {Function} fn the method the event invokes.  If fn is
1158
             *  undefined, then all event handlers for the type of event are 
1159
             *  removed.
1160
            * @return {boolean} true if the unbind was successful, false 
1161
             *  otherwise.
1162
            * @static
1163
             */
1164
            removeListener: function(el, sType, fn) {
1165
                var i, len, li;
1166

    
1167
                sType = this._getType(sType);
1168

    
1169
                // The el argument can be a string
1170
                if (typeof el == "string") {
1171
                    el = this.getEl(el);
1172
                // The el argument can be an array of elements or element ids.
1173
                } else if ( this._isValidCollection(el)) {
1174
                    var ok = true;
1175
                    for (i=el.length-1; i>-1; i--) {
1176
                        ok = ( this.removeListener(el[i], sType, fn) && ok );
1177
                    }
1178
                    return ok;
1179
                }
1180

    
1181
                if (!fn || !fn.call) {
1182
                    //return false;
1183
                    return this.purgeElement(el, false, sType);
1184
                }
1185

    
1186
                if ("unload" == sType) {
1187

    
1188
                    for (i=unloadListeners.length-1; i>-1; i--) {
1189
                        li = unloadListeners[i];
1190
                        if (li && 
1191
                            li[0] == el && 
1192
                            li[1] == sType && 
1193
                            li[2] == fn) {
1194
                                unloadListeners.splice(i, 1);
1195
                                // unloadListeners[i]=null;
1196
                                return true;
1197
                        }
1198
                    }
1199

    
1200
                    return false;
1201
                }
1202

    
1203
                var cacheItem = null;
1204

    
1205
                // The index is a hidden parameter; needed to remove it from
1206
                // the method signature because it was tempting users to
1207
                // try and take advantage of it, which is not possible.
1208
                var index = arguments[3];
1209
  
1210
                if ("undefined" === typeof index) {
1211
                    index = this._getCacheIndex(listeners, el, sType, fn);
1212
                }
1213

    
1214
                if (index >= 0) {
1215
                    cacheItem = listeners[index];
1216
                }
1217

    
1218
                if (!el || !cacheItem) {
1219
                    return false;
1220
                }
1221

    
1222

    
1223
                var bCapture = cacheItem[this.CAPTURE] === true ? true : false;
1224

    
1225
                try {
1226
                    this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture);
1227
                } catch(ex) {
1228
                    this.lastError = ex;
1229
                    return false;
1230
                }
1231

    
1232
                // removed the wrapped handler
1233
                delete listeners[index][this.WFN];
1234
                delete listeners[index][this.FN];
1235
                listeners.splice(index, 1);
1236
                // listeners[index]=null;
1237

    
1238
                return true;
1239

    
1240
            },
1241

    
1242
            /**
1243
             * Returns the event's target element.  Safari sometimes provides
1244
             * a text node, and this is automatically resolved to the text
1245
             * node's parent so that it behaves like other browsers.
1246
            * @method getTarget
1247
            * @param {Event} ev the event
1248
            * @param {boolean} resolveTextNode when set to true the target's
1249
             *                  parent will be returned if the target is a 
1250
             *                  text node.  @deprecated, the text node is
1251
             *                  now resolved automatically
1252
            * @return {HTMLElement} the event's target
1253
            * @static
1254
             */
1255
            getTarget: function(ev, resolveTextNode) {
1256
                var t = ev.target || ev.srcElement;
1257
                return this.resolveTextNode(t);
1258
            },
1259

    
1260
            /**
1261
             * In some cases, some browsers will return a text node inside
1262
             * the actual element that was targeted.  This normalizes the
1263
             * return value for getTarget and getRelatedTarget.
1264
            * @method resolveTextNode
1265
            * @param {HTMLElement} node node to resolve
1266
            * @return {HTMLElement} the normized node
1267
            * @static
1268
             */
1269
            resolveTextNode: function(n) {
1270
                try {
1271
                    if (n && 3 == n.nodeType) {
1272
                        return n.parentNode;
1273
                    }
1274
                } catch(e) { }
1275

    
1276
                return n;
1277
            },
1278

    
1279
            /**
1280
             * Returns the event's pageX
1281
            * @method getPageX
1282
            * @param {Event} ev the event
1283
            * @return {int} the event's pageX
1284
            * @static
1285
             */
1286
            getPageX: function(ev) {
1287
                var x = ev.pageX;
1288
                if (!x && 0 !== x) {
1289
                    x = ev.clientX || 0;
1290

    
1291
                    if ( this.isIE ) {
1292
                        x += this._getScrollLeft();
1293
                    }
1294
                }
1295

    
1296
                return x;
1297
            },
1298

    
1299
            /**
1300
             * Returns the event's pageY
1301
            * @method getPageY
1302
            * @param {Event} ev the event
1303
            * @return {int} the event's pageY
1304
            * @static
1305
             */
1306
            getPageY: function(ev) {
1307
                var y = ev.pageY;
1308
                if (!y && 0 !== y) {
1309
                    y = ev.clientY || 0;
1310

    
1311
                    if ( this.isIE ) {
1312
                        y += this._getScrollTop();
1313
                    }
1314
                }
1315

    
1316

    
1317
                return y;
1318
            },
1319

    
1320
            /**
1321
             * Returns the pageX and pageY properties as an indexed array.
1322
            * @method getXY
1323
            * @param {Event} ev the event
1324
            * @return {[x, y]} the pageX and pageY properties of the event
1325
            * @static
1326
             */
1327
            getXY: function(ev) {
1328
                return [this.getPageX(ev), this.getPageY(ev)];
1329
            },
1330

    
1331
            /**
1332
             * Returns the event's related target 
1333
            * @method getRelatedTarget
1334
            * @param {Event} ev the event
1335
            * @return {HTMLElement} the event's relatedTarget
1336
            * @static
1337
             */
1338
            getRelatedTarget: function(ev) {
1339
                var t = ev.relatedTarget;
1340
                if (!t) {
1341
                    if (ev.type == "mouseout") {
1342
                        t = ev.toElement;
1343
                    } else if (ev.type == "mouseover") {
1344
                        t = ev.fromElement;
1345
                    }
1346
                }
1347

    
1348
                return this.resolveTextNode(t);
1349
            },
1350

    
1351
            /**
1352
             * Returns the time of the event.  If the time is not included, the
1353
             * event is modified using the current time.
1354
            * @method getTime
1355
            * @param {Event} ev the event
1356
            * @return {Date} the time of the event
1357
            * @static
1358
             */
1359
            getTime: function(ev) {
1360
                if (!ev.time) {
1361
                    var t = new Date().getTime();
1362
                    try {
1363
                        ev.time = t;
1364
                    } catch(ex) { 
1365
                        this.lastError = ex;
1366
                        return t;
1367
                    }
1368
                }
1369

    
1370
                return ev.time;
1371
            },
1372

    
1373
            /**
1374
             * Convenience method for stopPropagation + preventDefault
1375
            * @method stopEvent
1376
            * @param {Event} ev the event
1377
            * @static
1378
             */
1379
            stopEvent: function(ev) {
1380
                this.stopPropagation(ev);
1381
                this.preventDefault(ev);
1382
            },
1383

    
1384
            /**
1385
             * Stops event propagation
1386
            * @method stopPropagation
1387
            * @param {Event} ev the event
1388
            * @static
1389
             */
1390
            stopPropagation: function(ev) {
1391
                if (ev.stopPropagation) {
1392
                    ev.stopPropagation();
1393
                } else {
1394
                    ev.cancelBubble = true;
1395
                }
1396
            },
1397

    
1398
            /**
1399
             * Prevents the default behavior of the event
1400
            * @method preventDefault
1401
            * @param {Event} ev the event
1402
            * @static
1403
             */
1404
            preventDefault: function(ev) {
1405
                if (ev.preventDefault) {
1406
                    ev.preventDefault();
1407
                } else {
1408
                    ev.returnValue = false;
1409
                }
1410
            },
1411
             
1412
            /**
1413
             * Finds the event in the window object, the caller's arguments, or
1414
             * in the arguments of another method in the callstack.  This is
1415
             * executed automatically for events registered through the event
1416
             * manager, so the implementer should not normally need to execute
1417
             * this function at all.
1418
            * @method getEvent
1419
            * @param {Event} e the event parameter from the handler
1420
            * @param {HTMLElement} boundEl the element the listener is attached to
1421
            * @return {Event} the event 
1422
            * @static
1423
             */
1424
            getEvent: function(e, boundEl) {
1425
                var ev = e || window.event;
1426

    
1427
                if (!ev) {
1428
                    var c = this.getEvent.caller;
1429
                    while (c) {
1430
                        ev = c.arguments[0];
1431
                        if (ev && Event == ev.constructor) {
1432
                            break;
1433
                        }
1434
                        c = c.caller;
1435
                    }
1436
                }
1437

    
1438
                return ev;
1439
            },
1440

    
1441
            /**
1442
             * Returns the charcode for an event
1443
            * @method getCharCode
1444
            * @param {Event} ev the event
1445
            * @return {int} the event's charCode
1446
            * @static
1447
             */
1448
            getCharCode: function(ev) {
1449
                var code = ev.keyCode || ev.charCode || 0;
1450

    
1451
                // webkit key normalization
1452
                if (YAHOO.env.ua.webkit && (code in webkitKeymap)) {
1453
                    code = webkitKeymap[code];
1454
                }
1455
                return code;
1456
            },
1457

    
1458
            /**
1459
             * Locating the saved event handler data by function ref
1460
             *
1461
            * @method _getCacheIndex
1462
            * @static
1463
            * @private
1464
             */
1465
            _getCacheIndex: function(a, el, sType, fn) {
1466
                for (var i=0, l=a.length; i<l; i=i+1) {
1467
                    var li = a[i];
1468
                    if ( li                 && 
1469
                         li[this.FN] == fn  && 
1470
                         li[this.EL] == el  && 
1471
                         li[this.TYPE] == sType ) {
1472
                        return i;
1473
                    }
1474
                }
1475

    
1476
                return -1;
1477
            },
1478

    
1479
            /**
1480
             * Generates an unique ID for the element if it does not already 
1481
             * have one.
1482
            * @method generateId
1483
            * @param el the element to create the id for
1484
            * @return {string} the resulting id of the element
1485
            * @static
1486
             */
1487
            generateId: function(el) {
1488
                var id = el.id;
1489

    
1490
                if (!id) {
1491
                    id = "yuievtautoid-" + counter;
1492
                    ++counter;
1493
                    el.id = id;
1494
                }
1495

    
1496
                return id;
1497
            },
1498

    
1499

    
1500
            /**
1501
             * We want to be able to use getElementsByTagName as a collection
1502
             * to attach a group of events to.  Unfortunately, different 
1503
             * browsers return different types of collections.  This function
1504
             * tests to determine if the object is array-like.  It will also 
1505
             * fail if the object is an array, but is empty.
1506
            * @method _isValidCollection
1507
            * @param o the object to test
1508
            * @return {boolean} true if the object is array-like and populated
1509
            * @static
1510
            * @private
1511
             */
1512
            _isValidCollection: function(o) {
1513
                try {
1514
                    return ( o                     && // o is something
1515
                             typeof o !== "string" && // o is not a string
1516
                             o.length              && // o is indexed
1517
                             !o.tagName            && // o is not an HTML element
1518
                             !o.alert              && // o is not a window
1519
                             typeof o[0] !== "undefined" );
1520
                } catch(ex) {
1521
                    return false;
1522
                }
1523

    
1524
            },
1525

    
1526
            /**
1527
            * @private
1528
            * @property elCache
1529
             * DOM element cache
1530
            * @static
1531
            * @deprecated Elements are not cached due to issues that arise when
1532
             * elements are removed and re-added
1533
             */
1534
            elCache: {},
1535

    
1536
            /**
1537
             * We cache elements bound by id because when the unload event 
1538
             * fires, we can no longer use document.getElementById
1539
            * @method getEl
1540
            * @static
1541
            * @private
1542
            * @deprecated Elements are not cached any longer
1543
             */
1544
            getEl: function(id) {
1545
                return (typeof id === "string") ? document.getElementById(id) : id;
1546
            },
1547

    
1548
            /**
1549
             * Clears the element cache
1550
            * @deprecated Elements are not cached any longer
1551
            * @method clearCache
1552
            * @static
1553
            * @private
1554
             */
1555
            clearCache: function() { },
1556

    
1557
            /**
1558
             * Custom event the fires when the dom is initially usable
1559
            * @event DOMReadyEvent
1560
             */
1561
            DOMReadyEvent: new YAHOO.util.CustomEvent("DOMReady", YAHOO, 0, 0, 1),
1562

    
1563
            /**
1564
             * hook up any deferred listeners
1565
            * @method _load
1566
            * @static
1567
            * @private
1568
             */
1569
            _load: function(e) {
1570

    
1571
                if (!loadComplete) {
1572
                    loadComplete = true;
1573
                    var EU = YAHOO.util.Event;
1574

    
1575
                    // Just in case DOMReady did not go off for some reason
1576
                    EU._ready();
1577

    
1578
                    // Available elements may not have been detected before the
1579
                    // window load event fires. Try to find them now so that the
1580
                    // the user is more likely to get the onAvailable notifications
1581
                    // before the window load notification
1582
                    EU._tryPreloadAttach();
1583

    
1584
                }
1585
            },
1586

    
1587
            /**
1588
             * Fires the DOMReady event listeners the first time the document is
1589
             * usable.
1590
            * @method _ready
1591
            * @static
1592
            * @private
1593
             */
1594
            _ready: function(e) {
1595
                var EU = YAHOO.util.Event;
1596
                if (!EU.DOMReady) {
1597
                    EU.DOMReady=true;
1598

    
1599
                    // Fire the content ready custom event
1600
                    EU.DOMReadyEvent.fire();
1601

    
1602
                    // Remove the DOMContentLoaded (FF/Opera)
1603
                    EU._simpleRemove(document, "DOMContentLoaded", EU._ready);
1604
                }
1605
            },
1606

    
1607
            /**
1608
             * Polling function that runs before the onload event fires, 
1609
             * attempting to attach to DOM Nodes as soon as they are 
1610
             * available
1611
            * @method _tryPreloadAttach
1612
            * @static
1613
            * @private
1614
             */
1615
            _tryPreloadAttach: function() {
1616

    
1617
                if (onAvailStack.length === 0) {
1618
                    retryCount = 0;
1619
                    if (this._interval) {
1620
                        // clearInterval(this._interval);
1621
                        this._interval.cancel();
1622
                        this._interval = null;
1623
                    } 
1624
                    return;
1625
                }
1626

    
1627
                if (this.locked) {
1628
                    return;
1629
                }
1630

    
1631
                if (this.isIE) {
1632
                    // Hold off if DOMReady has not fired and check current
1633
                    // readyState to protect against the IE operation aborted
1634
                    // issue.
1635
                    if (!this.DOMReady) {
1636
                        this.startInterval();
1637
                        return;
1638
                    }
1639
                }
1640

    
1641
                this.locked = true;
1642

    
1643

    
1644
                // keep trying until after the page is loaded.  We need to 
1645
                // check the page load state prior to trying to bind the 
1646
                // elements so that we can be certain all elements have been 
1647
                // tested appropriately
1648
                var tryAgain = !loadComplete;
1649
                if (!tryAgain) {
1650
                    tryAgain = (retryCount > 0 && onAvailStack.length > 0);
1651
                }
1652

    
1653
                // onAvailable
1654
                var notAvail = [];
1655

    
1656
                var executeItem = function (el, item) {
1657
                    var context = el;
1658
                    if (item.overrideContext) {
1659
                        if (item.overrideContext === true) {
1660
                            context = item.obj;
1661
                        } else {
1662
                            context = item.overrideContext;
1663
                        }
1664
                    }
1665
                    item.fn.call(context, item.obj);
1666
                };
1667

    
1668
                var i, len, item, el, ready=[];
1669

    
1670
                // onAvailable onContentReady
1671
                for (i=0, len=onAvailStack.length; i<len; i=i+1) {
1672
                    item = onAvailStack[i];
1673
                    if (item) {
1674
                        el = this.getEl(item.id);
1675
                        if (el) {
1676
                            if (item.checkReady) {
1677
                                if (loadComplete || el.nextSibling || !tryAgain) {
1678
                                    ready.push(item);
1679
                                    onAvailStack[i] = null;
1680
                                }
1681
                            } else {
1682
                                executeItem(el, item);
1683
                                onAvailStack[i] = null;
1684
                            }
1685
                        } else {
1686
                            notAvail.push(item);
1687
                        }
1688
                    }
1689
                }
1690
                
1691
                // make sure onContentReady fires after onAvailable
1692
                for (i=0, len=ready.length; i<len; i=i+1) {
1693
                    item = ready[i];
1694
                    executeItem(this.getEl(item.id), item);
1695
                }
1696

    
1697

    
1698
                retryCount--;
1699

    
1700
                if (tryAgain) {
1701
                    for (i=onAvailStack.length-1; i>-1; i--) {
1702
                        item = onAvailStack[i];
1703
                        if (!item || !item.id) {
1704
                            onAvailStack.splice(i, 1);
1705
                        }
1706
                    }
1707

    
1708
                    this.startInterval();
1709
                } else {
1710
                    if (this._interval) {
1711
                        // clearInterval(this._interval);
1712
                        this._interval.cancel();
1713
                        this._interval = null;
1714
                    }
1715
                }
1716

    
1717
                this.locked = false;
1718

    
1719
            },
1720

    
1721
            /**
1722
             * Removes all listeners attached to the given element via addListener.
1723
             * Optionally, the node's children can also be purged.
1724
             * Optionally, you can specify a specific type of event to remove.
1725
            * @method purgeElement
1726
            * @param {HTMLElement} el the element to purge
1727
            * @param {boolean} recurse recursively purge this element's children
1728
             * as well.  Use with caution.
1729
            * @param {string} sType optional type of listener to purge. If
1730
             * left out, all listeners will be removed
1731
            * @static
1732
             */
1733
            purgeElement: function(el, recurse, sType) {
1734
                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
1735
                var elListeners = this.getListeners(oEl, sType), i, len;
1736
                if (elListeners) {
1737
                    for (i=elListeners.length-1; i>-1; i--) {
1738
                        var l = elListeners[i];
1739
                        this.removeListener(oEl, l.type, l.fn);
1740
                    }
1741
                }
1742

    
1743
                if (recurse && oEl && oEl.childNodes) {
1744
                    for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
1745
                        this.purgeElement(oEl.childNodes[i], recurse, sType);
1746
                    }
1747
                }
1748
            },
1749

    
1750
            /**
1751
             * Returns all listeners attached to the given element via addListener.
1752
             * Optionally, you can specify a specific type of event to return.
1753
            * @method getListeners
1754
            * @param el {HTMLElement|string} the element or element id to inspect 
1755
            * @param sType {string} optional type of listener to return. If
1756
             * left out, all listeners will be returned
1757
            * @return {Object} the listener. Contains the following fields:
1758
             * &nbsp;&nbsp;type:   (string)   the type of event
1759
             * &nbsp;&nbsp;fn:     (function) the callback supplied to addListener
1760
             * &nbsp;&nbsp;obj:    (object)   the custom object supplied to addListener
1761
             * &nbsp;&nbsp;adjust: (boolean|object)  whether or not to adjust the default context
1762
             * &nbsp;&nbsp;scope: (boolean)  the derived context based on the adjust parameter
1763
             * &nbsp;&nbsp;index:  (int)      its position in the Event util listener cache
1764
            * @static
1765
             */           
1766
            getListeners: function(el, sType) {
1767
                var results=[], searchLists;
1768
                if (!sType) {
1769
                    searchLists = [listeners, unloadListeners];
1770
                } else if (sType === "unload") {
1771
                    searchLists = [unloadListeners];
1772
                } else {
1773
                    sType = this._getType(sType);
1774
                    searchLists = [listeners];
1775
                }
1776

    
1777
                var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el;
1778

    
1779
                for (var j=0;j<searchLists.length; j=j+1) {
1780
                    var searchList = searchLists[j];
1781
                    if (searchList) {
1782
                        for (var i=0,len=searchList.length; i<len ; ++i) {
1783
                            var l = searchList[i];
1784
                            if ( l  && l[this.EL] === oEl && 
1785
                                    (!sType || sType === l[this.TYPE]) ) {
1786
                                results.push({
1787
                                    type:   l[this.TYPE],
1788
                                    fn:     l[this.FN],
1789
                                    obj:    l[this.OBJ],
1790
                                    adjust: l[this.OVERRIDE],
1791
                                    scope:  l[this.ADJ_SCOPE],
1792
                                    index:  i
1793
                                });
1794
                            }
1795
                        }
1796
                    }
1797
                }
1798

    
1799
                return (results.length) ? results : null;
1800
            },
1801

    
1802
            /**
1803
             * Removes all listeners registered by pe.event.  Called 
1804
             * automatically during the unload event.
1805
            * @method _unload
1806
            * @static
1807
            * @private
1808
             */
1809
            _unload: function(e) {
1810

    
1811
                var EU = YAHOO.util.Event, i, j, l, len, index,
1812
                         ul = unloadListeners.slice(), context;
1813

    
1814
                // execute and clear stored unload listeners
1815
                for (i=0, len=unloadListeners.length; i<len; ++i) {
1816
                    l = ul[i];
1817
                    if (l) {
1818
                        context = window;
1819
                        if (l[EU.ADJ_SCOPE]) {
1820
                            if (l[EU.ADJ_SCOPE] === true) {
1821
                                context = l[EU.UNLOAD_OBJ];
1822
                            } else {
1823
                                context = l[EU.ADJ_SCOPE];
1824
                            }
1825
                        }
1826
                        l[EU.FN].call(context, EU.getEvent(e, l[EU.EL]), l[EU.UNLOAD_OBJ] );
1827
                        ul[i] = null;
1828
                    }
1829
                }
1830

    
1831
                l = null;
1832
                context = null;
1833
                unloadListeners = null;
1834

    
1835
                // Remove listeners to handle IE memory leaks
1836
                // 2.5.0 listeners are removed for all browsers again.  FireFox preserves
1837
                // at least some listeners between page refreshes, potentially causing
1838
                // errors during page load (mouseover listeners firing before they
1839
                // should if the user moves the mouse at the correct moment).
1840
                if (listeners) {
1841
                    for (j=listeners.length-1; j>-1; j--) {
1842
                        l = listeners[j];
1843
                        if (l) {
1844
                            EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j);
1845
                        } 
1846
                    }
1847
                    l=null;
1848
                }
1849

    
1850
                EU._simpleRemove(window, "unload", EU._unload);
1851

    
1852
            },
1853

    
1854
            /**
1855
             * Returns scrollLeft
1856
            * @method _getScrollLeft
1857
            * @static
1858
            * @private
1859
             */
1860
            _getScrollLeft: function() {
1861
                return this._getScroll()[1];
1862
            },
1863

    
1864
            /**
1865
             * Returns scrollTop
1866
            * @method _getScrollTop
1867
            * @static
1868
            * @private
1869
             */
1870
            _getScrollTop: function() {
1871
                return this._getScroll()[0];
1872
            },
1873

    
1874
            /**
1875
             * Returns the scrollTop and scrollLeft.  Used to calculate the 
1876
             * pageX and pageY in Internet Explorer
1877
            * @method _getScroll
1878
            * @static
1879
            * @private
1880
             */
1881
            _getScroll: function() {
1882
                var dd = document.documentElement, db = document.body;
1883
                if (dd && (dd.scrollTop || dd.scrollLeft)) {
1884
                    return [dd.scrollTop, dd.scrollLeft];
1885
                } else if (db) {
1886
                    return [db.scrollTop, db.scrollLeft];
1887
                } else {
1888
                    return [0, 0];
1889
                }
1890
            },
1891
            
1892
            /**
1893
             * Used by old versions of CustomEvent, restored for backwards
1894
             * compatibility
1895
            * @method regCE
1896
            * @private
1897
            * @static
1898
            * @deprecated still here for backwards compatibility
1899
             */
1900
            regCE: function() {},
1901

    
1902
            /**
1903
             * Adds a DOM event directly without the caching, cleanup, context adj, etc
1904
             *
1905
            * @method _simpleAdd
1906
            * @param {HTMLElement} el      the element to bind the handler to
1907
            * @param {string}      sType   the type of event handler
1908
            * @param {function}    fn      the callback to invoke
1909
            * @param {boolen}      capture capture or bubble phase
1910
            * @static
1911
            * @private
1912
             */
1913
            _simpleAdd: function () {
1914
                if (window.addEventListener) {
1915
                    return function(el, sType, fn, capture) {
1916
                        el.addEventListener(sType, fn, (capture));
1917
                    };
1918
                } else if (window.attachEvent) {
1919
                    return function(el, sType, fn, capture) {
1920
                        el.attachEvent("on" + sType, fn);
1921
                    };
1922
                } else {
1923
                    return function(){};
1924
                }
1925
            }(),
1926

    
1927
            /**
1928
             * Basic remove listener
1929
             *
1930
            * @method _simpleRemove
1931
            * @param {HTMLElement} el      the element to bind the handler to
1932
            * @param {string}      sType   the type of event handler
1933
            * @param {function}    fn      the callback to invoke
1934
            * @param {boolen}      capture capture or bubble phase
1935
            * @static
1936
            * @private
1937
             */
1938
            _simpleRemove: function() {
1939
                if (window.removeEventListener) {
1940
                    return function (el, sType, fn, capture) {
1941
                        el.removeEventListener(sType, fn, (capture));
1942
                    };
1943
                } else if (window.detachEvent) {
1944
                    return function (el, sType, fn) {
1945
                        el.detachEvent("on" + sType, fn);
1946
                    };
1947
                } else {
1948
                    return function(){};
1949
                }
1950
            }()
1951
        };
1952

    
1953
    }();
1954

    
1955
    (function() {
1956
        var EU = YAHOO.util.Event;
1957

    
1958
        /**
1959
         * YAHOO.util.Event.on is an alias for addListener
1960
        * @method on
1961
        * @see addListener
1962
        * @static
1963
         */
1964
        EU.on = EU.addListener;
1965

    
1966
        /**
1967
         * YAHOO.util.Event.onFocus is an alias for addFocusListener
1968
        * @method onFocus
1969
        * @see addFocusListener
1970
        * @static
1971
         * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type.
1972
         */
1973
        EU.onFocus = EU.addFocusListener;
1974

    
1975
        /**
1976
         * YAHOO.util.Event.onBlur is an alias for addBlurListener
1977
        * @method onBlur
1978
        * @see addBlurListener
1979
        * @static
1980
         * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type.
1981
         */     
1982
        EU.onBlur = EU.addBlurListener;
1983

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

    
1986
        // Internet Explorer: use the readyState of a defered script.
1987
        // This isolates what appears to be a safe moment to manipulate
1988
        // the DOM prior to when the document's readyState suggests
1989
        // it is safe to do so.
1990
        if (EU.isIE) {
1991
            if (self !== self.top) {
1992
                document.onreadystatechange = function() {
1993
                    if (document.readyState == 'complete') {
1994
                        document.onreadystatechange = null;
1995
                        EU._ready();
1996
                    }
1997
                };
1998
            } else {
1999

    
2000
                // Process onAvailable/onContentReady items when the 
2001
                // DOM is ready.
2002
                YAHOO.util.Event.onDOMReady(
2003
                        YAHOO.util.Event._tryPreloadAttach,
2004
                        YAHOO.util.Event, true);
2005
                
2006
                var n = document.createElement('p');  
2007

    
2008
                EU._dri = setInterval(function() {
2009
                    try {
2010
                        // throws an error if doc is not ready
2011
                        n.doScroll('left');
2012
                        clearInterval(EU._dri);
2013
                        EU._dri = null;
2014
                        EU._ready();
2015
                        n = null;
2016
                    } catch (ex) { 
2017
                    }
2018
                }, EU.POLL_INTERVAL); 
2019
            }
2020

    
2021
        // The document's readyState in Safari currently will
2022
        // change to loaded/complete before images are loaded.
2023
        } else if (EU.webkit && EU.webkit < 525) {
2024

    
2025
            EU._dri = setInterval(function() {
2026
                var rs=document.readyState;
2027
                if ("loaded" == rs || "complete" == rs) {
2028
                    clearInterval(EU._dri);
2029
                    EU._dri = null;
2030
                    EU._ready();
2031
                }
2032
            }, EU.POLL_INTERVAL); 
2033

    
2034
        // FireFox and Opera: These browsers provide a event for this
2035
        // moment.  The latest WebKit releases now support this event.
2036
        } else {
2037

    
2038
            EU._simpleAdd(document, "DOMContentLoaded", EU._ready);
2039

    
2040
        }
2041
        /////////////////////////////////////////////////////////////
2042

    
2043

    
2044
        EU._simpleAdd(window, "load", EU._load);
2045
        EU._simpleAdd(window, "unload", EU._unload);
2046
        EU._tryPreloadAttach();
2047
    })();
2048

    
2049
}
2050
/**
2051
 * EventProvider is designed to be used with YAHOO.augment to wrap 
2052
 * CustomEvents in an interface that allows events to be subscribed to 
2053
 * and fired by name.  This makes it possible for implementing code to
2054
 * subscribe to an event that either has not been created yet, or will
2055
 * not be created at all.
2056
 *
2057
 * @Class EventProvider
2058
 */
2059
YAHOO.util.EventProvider = function() { };
2060

    
2061
YAHOO.util.EventProvider.prototype = {
2062

    
2063
    /**
2064
     * Private storage of custom events
2065
    * @property __yui_events
2066
    * @type Object[]
2067
    * @private
2068
     */
2069
    __yui_events: null,
2070

    
2071
    /**
2072
     * Private storage of custom event subscribers
2073
    * @property __yui_subscribers
2074
    * @type Object[]
2075
    * @private
2076
     */
2077
    __yui_subscribers: null,
2078
    
2079
    /**
2080
     * Subscribe to a CustomEvent by event type
2081
     *
2082
    * @method subscribe
2083
    * @param p_type     {string}   the type, or name of the event
2084
    * @param p_fn       {function} the function to exectute when the event fires
2085
    * @param p_obj      {Object}   An object to be passed along when the event 
2086
     *                              fires
2087
    * @param overrideContext {boolean}  If true, the obj passed in becomes the 
2088
     *                              execution scope of the listener
2089
     */
2090
    subscribe: function(p_type, p_fn, p_obj, overrideContext) {
2091

    
2092
        this.__yui_events = this.__yui_events || {};
2093
        var ce = this.__yui_events[p_type];
2094

    
2095
        if (ce) {
2096
            ce.subscribe(p_fn, p_obj, overrideContext);
2097
        } else {
2098
            this.__yui_subscribers = this.__yui_subscribers || {};
2099
            var subs = this.__yui_subscribers;
2100
            if (!subs[p_type]) {
2101
                subs[p_type] = [];
2102
            }
2103
            subs[p_type].push(
2104
                { fn: p_fn, obj: p_obj, overrideContext: overrideContext } );
2105
        }
2106
    },
2107

    
2108
    /**
2109
     * Unsubscribes one or more listeners the from the specified event
2110
    * @method unsubscribe
2111
    * @param p_type {string}   The type, or name of the event.  If the type
2112
     *                          is not specified, it will attempt to remove
2113
     *                          the listener from all hosted events.
2114
    * @param p_fn   {Function} The subscribed function to unsubscribe, if not
2115
     *                          supplied, all subscribers will be removed.
2116
    * @param p_obj  {Object}   The custom object passed to subscribe.  This is
2117
     *                        optional, but if supplied will be used to
2118
     *                        disambiguate multiple listeners that are the same
2119
     *                        (e.g., you subscribe many object using a function
2120
     *                        that lives on the prototype)
2121
    * @return {boolean} true if the subscriber was found and detached.
2122
     */
2123
    unsubscribe: function(p_type, p_fn, p_obj) {
2124
        this.__yui_events = this.__yui_events || {};
2125
        var evts = this.__yui_events;
2126
        if (p_type) {
2127
            var ce = evts[p_type];
2128
            if (ce) {
2129
                return ce.unsubscribe(p_fn, p_obj);
2130
            }
2131
        } else {
2132
            var ret = true;
2133
            for (var i in evts) {
2134
                if (YAHOO.lang.hasOwnProperty(evts, i)) {
2135
                    ret = ret && evts[i].unsubscribe(p_fn, p_obj);
2136
                }
2137
            }
2138
            return ret;
2139
        }
2140

    
2141
        return false;
2142
    },
2143
    
2144
    /**
2145
     * Removes all listeners from the specified event.  If the event type
2146
     * is not specified, all listeners from all hosted custom events will
2147
     * be removed.
2148
    * @method unsubscribeAll
2149
    * @param p_type {string}   The type, or name of the event
2150
     */
2151
    unsubscribeAll: function(p_type) {
2152
        return this.unsubscribe(p_type);
2153
    },
2154

    
2155
    /**
2156
     * Creates a new custom event of the specified type.  If a custom event
2157
     * by that name already exists, it will not be re-created.  In either
2158
     * case the custom event is returned. 
2159
     *
2160
    * @method createEvent
2161
     *
2162
    * @param p_type {string} the type, or name of the event
2163
    * @param p_config {object} optional config params.  Valid properties are:
2164
     *
2165
     *  <ul>
2166
     *    <li>
2167
     *      scope: defines the default execution scope.  If not defined
2168
     *      the default scope will be this instance.
2169
     *    </li>
2170
     *    <li>
2171
     *      silent: if true, the custom event will not generate log messages.
2172
     *      This is false by default.
2173
     *    </li>
2174
     *    <li>
2175
     *      fireOnce: if true, the custom event will only notify subscribers
2176
     *      once regardless of the number of times the event is fired.  In
2177
     *      addition, new subscribers will be executed immediately if the
2178
     *      event has already fired.
2179
     *      This is false by default.
2180
     *    </li>
2181
     *    <li>
2182
     *      onSubscribeCallback: specifies a callback to execute when the
2183
     *      event has a new subscriber.  This will fire immediately for
2184
     *      each queued subscriber if any exist prior to the creation of
2185
     *      the event.
2186
     *    </li>
2187
     *  </ul>
2188
     *
2189
     *  @return {CustomEvent} the custom event
2190
     *
2191
     */
2192
    createEvent: function(p_type, p_config) {
2193

    
2194
        this.__yui_events = this.__yui_events || {};
2195
        var opts = p_config || {},
2196
            events = this.__yui_events, ce;
2197

    
2198
        if (events[p_type]) {
2199
        } else {
2200

    
2201
            ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent,
2202
                         YAHOO.util.CustomEvent.FLAT, opts.fireOnce);
2203

    
2204
            events[p_type] = ce;
2205

    
2206
            if (opts.onSubscribeCallback) {
2207
                ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
2208
            }
2209

    
2210
            this.__yui_subscribers = this.__yui_subscribers || {};
2211
            var qs = this.__yui_subscribers[p_type];
2212

    
2213
            if (qs) {
2214
                for (var i=0; i<qs.length; ++i) {
2215
                    ce.subscribe(qs[i].fn, qs[i].obj, qs[i].overrideContext);
2216
                }
2217
            }
2218
        }
2219

    
2220
        return events[p_type];
2221
    },
2222

    
2223

    
2224
   /**
2225
     * Fire a custom event by name.  The callback functions will be executed
2226
     * from the scope specified when the event was created, and with the 
2227
     * following parameters:
2228
     *   <ul>
2229
     *   <li>The first argument fire() was executed with</li>
2230
     *   <li>The custom object (if any) that was passed into the subscribe() 
2231
     *       method</li>
2232
     *   </ul>
2233
    * @method fireEvent
2234
    * @param p_type    {string}  the type, or name of the event
2235
    * @param arguments {Object*} an arbitrary set of parameters to pass to 
2236
     *                            the handler.
2237
    * @return {boolean} the return value from CustomEvent.fire
2238
     *                   
2239
     */
2240
    fireEvent: function(p_type) {
2241

    
2242
        this.__yui_events = this.__yui_events || {};
2243
        var ce = this.__yui_events[p_type];
2244

    
2245
        if (!ce) {
2246
            return null;
2247
        }
2248

    
2249
        var args = [];
2250
        for (var i=1; i<arguments.length; ++i) {
2251
            args.push(arguments[i]);
2252
        }
2253
        return ce.fire.apply(ce, args);
2254
    },
2255

    
2256
    /**
2257
     * Returns true if the custom event of the provided type has been created
2258
     * with createEvent.
2259
    * @method hasEvent
2260
    * @param type {string} the type, or name of the event
2261
     */
2262
    hasEvent: function(type) {
2263
        if (this.__yui_events) {
2264
            if (this.__yui_events[type]) {
2265
                return true;
2266
            }
2267
        }
2268
        return false;
2269
    }
2270

    
2271
};
2272

    
2273
(function() {
2274

    
2275
    var Event = YAHOO.util.Event, Lang = YAHOO.lang;
2276

    
2277
/**
2278
* KeyListener is a utility that provides an easy interface for listening for
2279
* keydown/keyup events fired against DOM elements.
2280
* @namespace YAHOO.util
2281
* @class KeyListener
2282
* @constructor
2283
* @param {HTMLElement} attachTo The element or element ID to which the key 
2284
*                               event should be attached
2285
* @param {String}      attachTo The element or element ID to which the key
2286
*                               event should be attached
2287
* @param {Object}      keyData  The object literal representing the key(s) 
2288
*                               to detect. Possible attributes are 
2289
*                               shift(boolean), alt(boolean), ctrl(boolean) 
2290
*                               and keys(either an int or an array of ints 
2291
*                               representing keycodes).
2292
* @param {Function}    handler  The CustomEvent handler to fire when the 
2293
*                               key event is detected
2294
* @param {Object}      handler  An object literal representing the handler. 
2295
* @param {String}      event    Optional. The event (keydown or keyup) to 
2296
*                               listen for. Defaults automatically to keydown.
2297
*
2298
* @knownissue the "keypress" event is completely broken in Safari 2.x and below.
2299
*             the workaround is use "keydown" for key listening.  However, if
2300
*             it is desired to prevent the default behavior of the keystroke,
2301
*             that can only be done on the keypress event.  This makes key
2302
*             handling quite ugly.
2303
* @knownissue keydown is also broken in Safari 2.x and below for the ESC key.
2304
*             There currently is no workaround other than choosing another
2305
*             key to listen for.
2306
*/
2307
YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
2308
    if (!attachTo) {
2309
    } else if (!keyData) {
2310
    } else if (!handler) {
2311
    } 
2312
    
2313
    if (!event) {
2314
        event = YAHOO.util.KeyListener.KEYDOWN;
2315
    }
2316

    
2317
    /**
2318
    * The CustomEvent fired internally when a key is pressed
2319
   * @event keyEvent
2320
   * @private
2321
   * @param {Object} keyData The object literal representing the key(s) to 
2322
    *                         detect. Possible attributes are shift(boolean), 
2323
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2324
    *                         int or an array of ints representing keycodes).
2325
    */
2326
    var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
2327
    
2328
    /**
2329
    * The CustomEvent fired when the KeyListener is enabled via the enable() 
2330
    * function
2331
   * @event enabledEvent
2332
   * @param {Object} keyData The object literal representing the key(s) to 
2333
    *                         detect. Possible attributes are shift(boolean), 
2334
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2335
    *                         int or an array of ints representing keycodes).
2336
    */
2337
    this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
2338

    
2339
    /**
2340
    * The CustomEvent fired when the KeyListener is disabled via the 
2341
    * disable() function
2342
   * @event disabledEvent
2343
   * @param {Object} keyData The object literal representing the key(s) to 
2344
    *                         detect. Possible attributes are shift(boolean), 
2345
    *                         alt(boolean), ctrl(boolean) and keys(either an 
2346
    *                         int or an array of ints representing keycodes).
2347
    */
2348
    this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
2349

    
2350
    if (Lang.isString(attachTo)) {
2351
        attachTo = document.getElementById(attachTo); // No Dom util
2352
    }
2353

    
2354
    if (Lang.isFunction(handler)) {
2355
        keyEvent.subscribe(handler);
2356
    } else {
2357
        keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
2358
    }
2359

    
2360
    /**
2361
    * Handles the key event when a key is pressed.
2362
   * @method handleKeyPress
2363
   * @param {DOMEvent} e   The keypress DOM event
2364
   * @param {Object}   obj The DOM event scope object
2365
   * @private
2366
    */
2367
    function handleKeyPress(e, obj) {
2368
        if (! keyData.shift) {  
2369
            keyData.shift = false; 
2370
        }
2371
        if (! keyData.alt) {    
2372
            keyData.alt = false;
2373
        }
2374
        if (! keyData.ctrl) {
2375
            keyData.ctrl = false;
2376
        }
2377

    
2378
        // check held down modifying keys first
2379
        if (e.shiftKey == keyData.shift && 
2380
            e.altKey   == keyData.alt &&
2381
            e.ctrlKey  == keyData.ctrl) { // if we pass this, all modifiers match
2382
            
2383
            var dataItem, keys = keyData.keys, key;
2384

    
2385
            if (YAHOO.lang.isArray(keys)) {
2386
                for (var i=0;i<keys.length;i++) {
2387
                    dataItem = keys[i];
2388
                    key = Event.getCharCode(e);
2389

    
2390
                    if (dataItem == key) {
2391
                        keyEvent.fire(key, e);
2392
                        break;
2393
                    }
2394
                }
2395
            } else {
2396
                key = Event.getCharCode(e);
2397
                if (keys == key ) {
2398
                    keyEvent.fire(key, e);
2399
                }
2400
            }
2401
        }
2402
    }
2403

    
2404
    /**
2405
    * Enables the KeyListener by attaching the DOM event listeners to the 
2406
    * target DOM element
2407
   * @method enable
2408
    */
2409
    this.enable = function() {
2410
        if (! this.enabled) {
2411
            Event.on(attachTo, event, handleKeyPress);
2412
            this.enabledEvent.fire(keyData);
2413
        }
2414
        /**
2415
        * Boolean indicating the enabled/disabled state of the Tooltip
2416
       * @property enabled
2417
       * @type Boolean
2418
        */
2419
        this.enabled = true;
2420
    };
2421

    
2422
    /**
2423
    * Disables the KeyListener by removing the DOM event listeners from the 
2424
    * target DOM element
2425
   * @method disable
2426
    */
2427
    this.disable = function() {
2428
        if (this.enabled) {
2429
            Event.removeListener(attachTo, event, handleKeyPress);
2430
            this.disabledEvent.fire(keyData);
2431
        }
2432
        this.enabled = false;
2433
    };
2434

    
2435
    /**
2436
    * Returns a String representation of the object.
2437
   * @method toString
2438
   * @return {String}  The string representation of the KeyListener
2439
    */ 
2440
    this.toString = function() {
2441
        return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + 
2442
                (attachTo.id ? "[" + attachTo.id + "]" : "");
2443
    };
2444

    
2445
};
2446

    
2447
var KeyListener = YAHOO.util.KeyListener;
2448

    
2449
/**
2450
 * Constant representing the DOM "keydown" event.
2451
 * @property YAHOO.util.KeyListener.KEYDOWN
2452
 * @static
2453
 * @final
2454
 * @type String
2455
 */
2456
KeyListener.KEYDOWN = "keydown";
2457

    
2458
/**
2459
 * Constant representing the DOM "keyup" event.
2460
 * @property YAHOO.util.KeyListener.KEYUP
2461
 * @static
2462
 * @final
2463
 * @type String
2464
 */
2465
KeyListener.KEYUP = "keyup";
2466

    
2467
/**
2468
 * keycode constants for a subset of the special keys
2469
 * @property KEY
2470
 * @static
2471
 * @final
2472
 */
2473
KeyListener.KEY = {
2474
    ALT          : 18,
2475
    BACK_SPACE   : 8,
2476
    CAPS_LOCK    : 20,
2477
    CONTROL      : 17,
2478
    DELETE       : 46,
2479
    DOWN         : 40,
2480
    END          : 35,
2481
    ENTER        : 13,
2482
    ESCAPE       : 27,
2483
    HOME         : 36,
2484
    LEFT         : 37,
2485
    META         : 224,
2486
    NUM_LOCK     : 144,
2487
    PAGE_DOWN    : 34,
2488
    PAGE_UP      : 33, 
2489
    PAUSE        : 19,
2490
    PRINTSCREEN  : 44,
2491
    RIGHT        : 39,
2492
    SCROLL_LOCK  : 145,
2493
    SHIFT        : 16,
2494
    SPACE        : 32,
2495
    TAB          : 9,
2496
    UP           : 38
2497
};
2498

    
2499
})();
2500
YAHOO.register("event", YAHOO.util.Event, {version: "2.8.0r4", build: "2449"});
(4-4/5)