source: Dev/trunk/d3/lib/env-js/envjs/event.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 19.9 KB
Line 
1/*
2 * Envjs event.1.3.pre03
3 * Pure JavaScript Browser Environment
4 * By John Resig <http://ejohn.org/> and the Envjs Team
5 * Copyright 2008-2010 John Resig, under the MIT License
6 *
7 * This file simply provides the global definitions we need to
8 * be able to correctly implement to core browser DOM Event interfaces.
9
10 - leaked globally -
11
12var Event,
13    MouseEvent,
14    UIEvent,
15    KeyboardEvent,
16    MutationEvent,
17    DocumentEvent,
18    EventTarget,
19    EventException;
20   
21 */
22
23var Envjs = Envjs || require('./platform/core').Envjs,
24        After = After || require('./platform/core').After,
25        Document = Document || require('./dom').Document;
26/*
27 * Envjs event.1.3.pre03
28 * Pure JavaScript Browser Environment
29 * By John Resig <http://ejohn.org/> and the Envjs Team
30 * Copyright 2008-2010 John Resig, under the MIT License
31 */
32
33//CLOSURE_START
34(function(){
35
36
37
38
39
40/**
41 * @author john resig
42 */
43// Helper method for extending one object with another.
44function __extend__(a,b) {
45    for ( var i in b ) {
46        if(b.hasOwnProperty(i)){
47            var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
48            if ( g || s ) {
49                if ( g ) { a.__defineGetter__(i, g); }
50                if ( s ) { a.__defineSetter__(i, s); }
51            } else {
52                a[i] = b[i];
53            }
54        }
55    }
56    return a;
57}
58
59/**
60 * @author john resig
61 */
62//from jQuery
63function __setArray__( target, array ) {
64    // Resetting the length to 0, then using the native Array push
65    // is a super-fast way to populate an object with array-like properties
66    target.length = 0;
67    Array.prototype.push.apply( target, array );
68}
69var __addEventListener__,
70    __removeEventListener__,
71    __dispatchEvent__,
72    __captureEvent__,
73    __bubbleEvent__;
74
75(function(){
76   
77var log = Envjs.logger();
78
79Envjs.once('tick', function(){
80   log = Envjs.logger('Envjs.DOM.EventTarget').debug('available');
81});
82
83/**
84 * @name EventTarget
85 * @w3c:domlevel 2
86 * @uri -//TODO: paste dom event level 2 w3c spc uri here
87 */
88exports.EventTarget = EventTarget = function(){};
89EventTarget.prototype.addEventListener = function(type, fn, phase){
90    __addEventListener__(this, type, fn, phase);
91};
92EventTarget.prototype.removeEventListener = function(type, fn, phase){
93    __removeEventListener__(this, type, fn, phase);
94};
95EventTarget.prototype.dispatchEvent = function(event, bubbles){
96    __dispatchEvent__(this, event, bubbles);
97};
98
99__extend__(Node.prototype, EventTarget.prototype);
100
101var $events = [{}];
102
103__addEventListener__ = function(target, type, fn, phase){
104    phase = !!phase?"CAPTURING":"BUBBLING";
105    if ( !target.uuid ) {
106        target.uuid = $events.length+'';
107        log.debug('add event uuid for %s %s', target, target.uuid);
108    }
109    if ( !$events[target.uuid] ) {
110        log.debug('creating listener for target: %s %s', target, target.uuid);
111        $events[target.uuid] = {};
112    }
113    if ( !$events[target.uuid][type] ){
114        log.debug('creating listener for type: %s %s %s', target, target.uuid, type);
115        $events[target.uuid][type] = {
116            CAPTURING:[],
117            BUBBLING:[]
118        };
119    }
120    if ( $events[target.uuid][type][phase].indexOf( fn ) < 0 ){
121        log.debug( 'adding event listener %s %s %s %s', target, target.uuid, type, phase);
122        $events[target.uuid][type][phase].push( fn );
123    }
124    log.debug('registered event listeners %s', $events.length);
125};
126
127__removeEventListener__ = function(target, type, fn, phase){
128    phase = !!phase?"CAPTURING":"BUBBLING";
129    if ( !target.uuid ) {
130        log.debug('target has never had registered events %s', target);
131        return;
132    }
133    if ( !$events[target.uuid] ) {
134        log.debug('target has no registered events to remove %s %s', target, target.uuid);
135        return;
136    }
137    if(type == '*'){
138        //used to clean all event listeners for a given node
139        log.debug('cleaning all event listeners for node %s %s',target, target.uuid);
140        delete $events[target.uuid];
141        return;
142    }else if ( !$events[target.uuid][type] ){
143        log.debug('target has no registered events of type %s to remove %s %s', type, target, target.uuid);
144        return;
145    }
146    $events[target.uuid][type][phase] =
147        $events[target.uuid][type][phase].filter(function(f){
148            log.debug('removing event listener %s %s %s %s',  target, type, phase );
149            return f != fn;
150        });
151};
152
153var __eventuuid__ = 0;
154
155__dispatchEvent__ = function(target, event, bubbles){
156   
157    if (!event.uuid) {
158        event.uuid = __eventuuid__++;
159    }
160    //the window scope defines the $event object, for IE(^^^) compatibility;
161    //$event = event;
162
163    if (bubbles === undefined || bubbles === null) {
164        bubbles = true;
165    }
166
167    if (!event.target) {
168        event.target = target;
169    }
170
171    log.debug('dispatching %s %s %s %s', event.uuid, target, event.type, bubbles);
172    if ( event.type && (target.nodeType || target === window )) {
173
174        __captureEvent__(target, event);
175
176        event.eventPhase = Event.AT_TARGET;
177        if ( target.uuid && $events[target.uuid] && $events[target.uuid][event.type] ) {
178            event.currentTarget = target;
179
180            log.debug('begin dispatching capturing phase %s %s', target, event.type);
181            $events[target.uuid][event.type].CAPTURING.forEach(function(fn){
182                log.debug('capturing event %s', target);
183                var returnValue = fn.apply(target, [event]);
184                if(returnValue === false){
185                    event.stopPropagation();
186                }
187            });
188
189            log.debug('begin dispatching bubbling phase %s %s', target, event.type);
190            $events[target.uuid][event.type].BUBBLING.forEach(function(fn){
191                log.debug('bubbling event %s', target);
192                var returnValue = fn.apply(target, [event] );
193                if(returnValue === false){
194                    event.stopPropagation();
195                }
196            });
197        }
198        if (target["on" + event.type]) {
199            target["on" + event.type](event);
200        }
201        if (bubbles && !event.cancelled){
202            __bubbleEvent__(target, event);
203        }
204        if(!event._preventDefault){
205            //At this point I'm guessing that just HTMLEvents are concerned
206            //with default behavior being executed in a browser but I could be
207            //wrong as usual.  The goal is much more to filter at this point
208            //what events have no need to be handled
209            //console.log('triggering default behavior for %s', event.type);
210            if(event.type in Envjs.defaultEventBehaviors){
211                Envjs.defaultEventBehaviors[event.type](event);
212            }
213        }
214        log.debug('deleting event %s', event.uuid);
215        event.target = null;
216        event = null;
217    }else{
218        throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR);
219    }
220};
221
222__captureEvent__ = function(target, event){
223    var ancestorStack = [],
224        parent = target.parentNode,
225        stopevent = function(fn){
226            var returnValue = fn( event );
227            if(returnValue === false){
228                event.stopPropagation();
229            }
230        };
231
232    event.eventPhase = Event.CAPTURING_PHASE;
233    while(parent){
234        if(parent.uuid && $events[parent.uuid] && $events[parent.uuid][event.type]){
235            ancestorStack.push(parent);
236        }
237        parent = parent.parentNode;
238    }
239    while(ancestorStack.length && !event.cancelled){
240        event.currentTarget = ancestorStack.pop();
241        if($events[event.currentTarget.uuid] && $events[event.currentTarget.uuid][event.type]){
242            $events[event.currentTarget.uuid][event.type].CAPTURING.forEach(stopevent);
243        }
244    }
245};
246
247__bubbleEvent__ = function(target, event){
248    var parent = target.parentNode,
249        stopevent = function(fn){
250            var returnValue = fn( event );
251            if(returnValue === false){
252                event.stopPropagation();
253            }
254        };
255    event.eventPhase = Event.BUBBLING_PHASE;
256    while(parent){
257        if(parent.uuid && $events[parent.uuid] && $events[parent.uuid][event.type] ){
258            event.currentTarget = parent;
259            $events[event.currentTarget.uuid][event.type].BUBBLING.forEach(stopevent);
260        }
261        parent = parent.parentNode;
262    }
263};
264
265}(/*Envjs.DOM2.EventTarget*/));
266
267
268
269
270(function(){
271   
272var log = Envjs.logger();
273
274Envjs.once('tick', function(){
275   log = Envjs.logger('Envjs.DOM.Event').debug('available');
276});
277
278/**
279 * @class Event
280 */
281exports.Event = Event = function(options){
282    // event state is kept read-only by forcing
283    // a new object for each event.  This may not
284    // be appropriate in the long run and we'll
285    // have to decide if we simply dont adhere to
286    // the read-only restriction of the specification
287    this._bubbles = true;
288    this._cancelable = true;
289    this._cancelled = false;
290    this._currentTarget = null;
291    this._target = null;
292    this._eventPhase = Event.AT_TARGET;
293    this._timeStamp = new Date().getTime();
294    this._preventDefault = false;
295    this._stopPropogation = false;
296};
297
298__extend__(Event.prototype,{
299    get bubbles(){return this._bubbles;},
300    get cancelable(){return this._cancelable;},
301    get currentTarget(){return this._currentTarget;},
302    set currentTarget(currentTarget){ this._currentTarget = currentTarget; },
303    get eventPhase(){return this._eventPhase;},
304    set eventPhase(eventPhase){this._eventPhase = eventPhase;},
305    get target(){return this._target;},
306    set target(target){ this._target = target;},
307    get timeStamp(){return this._timeStamp;},
308    get type(){return this._type;},
309    initEvent: function(type, bubbles, cancelable){
310        this._type=type?type:'';
311        this._bubbles=!!bubbles;
312        this._cancelable=!!cancelable;
313    },
314    preventDefault: function(){
315        this._preventDefault = true;
316    },
317    stopPropagation: function(){
318        if(this._cancelable){
319            this._cancelled = true;
320            this._bubbles = false;
321        }
322    },
323    get cancelled(){
324        return this._cancelled;
325    },
326    toString: function(){
327        return '[object Event]';
328    }
329});
330
331__extend__(Event,{
332    CAPTURING_PHASE : 1,
333    AT_TARGET       : 2,
334    BUBBLING_PHASE  : 3
335});
336
337}(/*Envjs.DOM.Event*/));
338
339
340
341(function(){
342   
343var log = Envjs.logger();
344
345Envjs.once('tick', function(){
346   log = Envjs.logger('Envjs.DOM.UIEvent').debug('available');
347});
348
349/**
350 * @name UIEvent
351 * @param {Object} options
352 */
353exports.UIEvent = UIEvent = function(options) {
354    this._view = null;
355    this._detail = 0;
356};
357
358UIEvent.prototype = new Event();
359__extend__(UIEvent.prototype,{
360    get view(){
361        return this._view;
362    },
363    get detail(){
364        return this._detail;
365    },
366    initUIEvent: function(type, bubbles, cancelable, windowObject, detail){
367        this.initEvent(type, bubbles, cancelable);
368        this._detail = 0;
369        this._view = windowObject;
370    }
371});
372
373}(/*Envjs.DOM.UIEvent*/));
374
375(function(){
376   
377var log = Envjs.logger();
378
379Envjs.once('tick', function(){
380   log = Envjs.logger('Envjs.DOM.MouseEvent').debug('available');
381});
382
383/**
384 * @name MouseEvent
385 * @w3c:domlevel 2
386 * @uri http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html
387 */
388exports.MouseEvent = MouseEvent = function(options) {
389    this._screenX= 0;
390    this._screenY= 0;
391    this._clientX= 0;
392    this._clientY= 0;
393    this._ctrlKey= false;
394    this._metaKey= false;
395    this._altKey= false;
396    this._button= null;
397    this._relatedTarget= null;
398};
399MouseEvent.prototype = new UIEvent();
400__extend__(MouseEvent.prototype,{
401    get screenX(){
402        return this._screenX;
403    },
404    get screenY(){
405        return this._screenY;
406    },
407    get clientX(){
408        return this._clientX;
409    },
410    get clientY(){
411        return this._clientY;
412    },
413    get ctrlKey(){
414        return this._ctrlKey;
415    },
416    get altKey(){
417        return this._altKey;
418    },
419    get shiftKey(){
420        return this._shiftKey;
421    },
422    get metaKey(){
423        return this._metaKey;
424    },
425    get button(){
426        return this._button;
427    },
428    get relatedTarget(){
429        return this._relatedTarget;
430    },
431    initMouseEvent: function(type, bubbles, cancelable, windowObject, detail,
432            screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey,
433            metaKey, button, relatedTarget){
434        this.initUIEvent(type, bubbles, cancelable, windowObject, detail);
435        this._screenX = screenX;
436        this._screenY = screenY;
437        this._clientX = clientX;
438        this._clientY = clientY;
439        this._ctrlKey = ctrlKey;
440        this._altKey = altKey;
441        this._shiftKey = shiftKey;
442        this._metaKey = metaKey;
443        this._button = button;
444        this._relatedTarget = relatedTarget;
445    }
446});
447
448}(/*Envjs.DOM2.MouseEvent*/));
449
450(function(){
451   
452var log = Envjs.logger();
453
454Envjs.once('tick', function(){
455   log = Envjs.logger('Envjs.DOM.KeyboardEvent').
456                debug('KeyboardEvent available');
457});
458
459/**
460 * Interface KeyboardEvent (introduced in DOM Level 3)
461 */
462exports.KeyboardEvent = KeyboardEvent = function(options) {
463    this._keyIdentifier = 0;
464    this._keyLocation = 0;
465    this._ctrlKey = false;
466    this._metaKey = false;
467    this._altKey = false;
468    this._metaKey = false;
469};
470KeyboardEvent.prototype = new UIEvent();
471
472__extend__(KeyboardEvent.prototype,{
473
474    get ctrlKey(){
475        return this._ctrlKey;
476    },
477    get altKey(){
478        return this._altKey;
479    },
480    get shiftKey(){
481        return this._shiftKey;
482    },
483    get metaKey(){
484        return this._metaKey;
485    },
486    get button(){
487        return this._button;
488    },
489    get relatedTarget(){
490        return this._relatedTarget;
491    },
492    getModifiersState: function(keyIdentifier){
493
494    },
495    initMouseEvent: function(type, bubbles, cancelable, windowObject,
496            keyIdentifier, keyLocation, modifiersList, repeat){
497        this.initUIEvent(type, bubbles, cancelable, windowObject, 0);
498        this._keyIdentifier = keyIdentifier;
499        this._keyLocation = keyLocation;
500        this._modifiersList = modifiersList;
501        this._repeat = repeat;
502    }
503});
504
505KeyboardEvent.DOM_KEY_LOCATION_STANDARD      = 0;
506KeyboardEvent.DOM_KEY_LOCATION_LEFT          = 1;
507KeyboardEvent.DOM_KEY_LOCATION_RIGHT         = 2;
508KeyboardEvent.DOM_KEY_LOCATION_NUMPAD        = 3;
509KeyboardEvent.DOM_KEY_LOCATION_MOBILE        = 4;
510KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK      = 5;
511
512}(/*Envjs.DOM3.KeyboardEvent*/));
513
514
515//We dont fire mutation events until someone has registered for them
516var __supportedMutations__ = /DOMSubtreeModified|DOMNodeInserted|DOMNodeRemoved|DOMAttrModified|DOMCharacterDataModified/;
517
518var __fireMutationEvents__ = Aspect.before({
519    target: EventTarget,
520    method: 'addEventListener'
521}, function(target, type){
522    if(type && type.match(__supportedMutations__)){
523        //unweaving removes the __addEventListener__ aspect
524        __fireMutationEvents__.unweave();
525        // These two methods are enough to cover all dom 2 manipulations
526        Aspect.around({
527            target: Node,
528            method:"removeChild"
529        }, function(invocation){
530            var event,
531                node = invocation['arguments'][0];
532            event = node.ownerDocument.createEvent('MutationEvents');
533            event.initEvent('DOMNodeRemoved', true, false, node.parentNode, null, null, null, null);
534            node.dispatchEvent(event, false);
535            return invocation.proceed();
536
537        });
538        Aspect.around({
539            target: Node,
540            method:"appendChild"
541        }, function(invocation) {
542            var event,
543                node = invocation.proceed();
544            event = node.ownerDocument.createEvent('MutationEvents');
545            event.initEvent('DOMNodeInserted', true, false, node.parentNode, null, null, null, null);
546            node.dispatchEvent(event, false);
547            return node;
548        });
549    }
550});
551
552(function(){
553   
554var log = Envjs.logger();
555
556Envjs.once('tick', function(){
557   log = Envjs.logger('Envjs.DOM.MutationEvent').debug('available');
558});
559
560/**
561 * @name MutationEvent
562 * @param {Object} options
563 */
564exports.MutationEvent = MutationEvent = function(options) {
565    this._cancelable = false;
566    this._timeStamp = 0;
567};
568
569MutationEvent.prototype = new Event();
570__extend__(MutationEvent.prototype,{
571    get relatedNode(){
572        return this._relatedNode;
573    },
574    get prevValue(){
575        return this._prevValue;
576    },
577    get newValue(){
578        return this._newValue;
579    },
580    get attrName(){
581        return this._attrName;
582    },
583    get attrChange(){
584        return this._attrChange;
585    },
586    initMutationEvent: function( type, bubbles, cancelable,
587            relatedNode, prevValue, newValue, attrName, attrChange ){
588        this._relatedNode = relatedNode;
589        this._prevValue = prevValue;
590        this._newValue = newValue;
591        this._attrName = attrName;
592        this._attrChange = attrChange;
593        switch(type){
594            case "DOMSubtreeModified":
595                this.initEvent(type, true, false);
596                break;
597            case "DOMNodeInserted":
598                this.initEvent(type, true, false);
599                break;
600            case "DOMNodeRemoved":
601                this.initEvent(type, true, false);
602                break;
603            case "DOMNodeRemovedFromDocument":
604                this.initEvent(type, false, false);
605                break;
606            case "DOMNodeInsertedIntoDocument":
607                this.initEvent(type, false, false);
608                break;
609            case "DOMAttrModified":
610                this.initEvent(type, true, false);
611                break;
612            case "DOMCharacterDataModified":
613                this.initEvent(type, true, false);
614                break;
615            default:
616                this.initEvent(type, bubbles, cancelable);
617        }
618    }
619});
620
621// constants
622MutationEvent.ADDITION = 0;
623MutationEvent.MODIFICATION = 1;
624MutationEvent.REMOVAL = 2;
625
626}(/*Envjs.DOM.MutationEvent*/));
627
628
629(function(){
630   
631var log = Envjs.logger();
632
633Envjs.once('tick', function(){
634   log = Envjs.logger('Envjs.DOM.EventException').debug('available');
635});
636
637/**
638 * @name EventException
639 */
640exports.EventException = EventException = function(code) {
641  this.code = code;
642};
643EventException.UNSPECIFIED_EVENT_TYPE_ERR = 0;
644
645}(/*Envjs.DOM2.EventException*/));
646/**
647 *
648 * DOM Level 2: http://www.w3.org/TR/DOM-Level-2-Events/events.html
649 * DOM Level 3: http://www.w3.org/TR/DOM-Level-3-Events/
650 *
651 * interface DocumentEvent {
652 *   Event createEvent (in DOMString eventType)
653 *      raises (DOMException);
654 * };
655 *
656 * Firefox (3.6) exposes DocumentEvent
657 * Safari (4) does NOT.
658 */
659
660(function(){
661   
662var log = Envjs.logger();
663
664Envjs.once('tick', function(){
665   log = Envjs.logger('Envjs.DOM.DocumentEvent').debug('available');
666});
667
668/**
669 * TODO: Not sure we need a full prototype.  We not just an regular object?
670 */
671exports.DocumentEvent = DocumentEvent = function(){};
672DocumentEvent.prototype.__EventMap__ = {
673    // Safari4: singular and plural forms accepted
674    // Firefox3.6: singular and plural forms accepted
675    'Event'          : Event,
676    'Events'         : Event,
677    'UIEvent'        : UIEvent,
678    'UIEvents'       : UIEvent,
679    'MouseEvent'     : MouseEvent,
680    'MouseEvents'    : MouseEvent,
681    'MutationEvent'  : MutationEvent,
682    'MutationEvents' : MutationEvent,
683
684    // Safari4: accepts HTMLEvents, but not HTMLEvent
685    // Firefox3.6: accepts HTMLEvents, but not HTMLEvent
686    'HTMLEvent'      : Event,
687    'HTMLEvents'     : Event,
688
689    // Safari4: both not accepted
690    // Firefox3.6, only KeyEvents is accepted
691    'KeyEvent'       : KeyboardEvent,
692    'KeyEvents'      : KeyboardEvent,
693
694    // Safari4: both accepted
695    // Firefox3.6: none accepted
696    'KeyboardEvent'  : KeyboardEvent,
697    'KeyboardEvents' : KeyboardEvent
698};
699
700DocumentEvent.prototype.createEvent = function(eventType) {
701    var Clazz = this.__EventMap__[eventType];
702    if (Clazz) {
703        return new Clazz();
704    }
705    throw(new DOMException(DOMException.NOT_SUPPORTED_ERR));
706};
707
708__extend__(Document.prototype, DocumentEvent.prototype);
709
710
711}(/*Envjs.DOM.DocumentEvent*/));
712
713/**
714 * @author john resig & the envjs team
715 * @uri http://www.envjs.com/
716 * @copyright 2008-2010
717 * @license MIT
718 */
719//CLOSURE_END
720}());
Note: See TracBrowser for help on using the repository browser.