add spec for 2493
[mootools.git] / Specs / Element / Element.Event.js
blob428a39b3424f3586a2dec4730e3ad26ac2cd0931
1 /*
2 ---
3 name: Element.Event
4 requires: ~
5 provides: ~
6 ...
7 */
9 (function(){
11 var Local = Local || {};
13 var fire = 'fireEvent', create = function(){
14         return new Element('div');
17 describe('Events API: Element', function(){
19         beforeEach(function(){
20                 Local.called = 0;
21                 Local.fn = function(){
22                         return Local.called++;
23                 };
24         });
26         it('should add an Event to the Class', function(){
27                 var object = create();
29                 object.addEvent('event', Local.fn)[fire]('event');
31                 expect(Local.called).toEqual(1);
32         });
34         it('should add multiple Events to the Class', function(){
35                 create().addEvents({
36                         event1: Local.fn,
37                         event2: Local.fn
38                 })[fire]('event1')[fire]('event2');
40                 expect(Local.called).toEqual(2);
41         });
43         it('should remove a specific method for an event', function(){
44                 var object = create();
45                 var x = 0, fn = function(){ x++; };
47                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvent('event', Local.fn)[fire]('event');
49                 expect(x).toEqual(1);
50                 expect(Local.called).toEqual(0);
51         });
53         it('should remove an event and its methods', function(){
54                 var object = create();
55                 var x = 0, fn = function(){ x++; };
57                 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvents('event')[fire]('event');
59                 expect(x).toEqual(0);
60                 expect(Local.called).toEqual(0);
61         });
63         it('should remove all events', function(){
64                 var object = create();
65                 var x = 0, fn = function(){ x++; };
67                 object.addEvent('event1', Local.fn).addEvent('event2', fn).removeEvents();
68                 object[fire]('event1')[fire]('event2');
70                 // Should not fail
71                 object.removeEvents()[fire]('event1')[fire]('event2');
73                 expect(x).toEqual(0);
74                 expect(Local.called).toEqual(0);
75         });
77         it('should remove events with an object', function(){
78                 var object = create();
79                 var events = {
80                         event1: Local.fn,
81                         event2: Local.fn
82                 };
84                 object.addEvent('event1', function(){ Local.fn(); }).addEvents(events)[fire]('event1');
85                 expect(Local.called).toEqual(2);
87                 object.removeEvents(events);
88                 object[fire]('event1');
89                 expect(Local.called).toEqual(3);
91                 object[fire]('event2');
92                 expect(Local.called).toEqual(3);
93         });
95         it('should remove an event immediately', function(){
96                 var object = create();
98                 var methods = [];
100                 var three = function(){
101                         methods.push(3);
102                 };
104                 object.addEvent('event', function(){
105                         methods.push(1);
106                         this.removeEvent('event', three);
107                 }).addEvent('event', function(){
108                         methods.push(2);
109                 }).addEvent('event', three);
111                 object[fire]('event');
112                 expect(methods).toEqual([1, 2]);
114                 object[fire]('event');
115                 expect(methods).toEqual([1, 2, 1, 2]);
116         });
118         it('should be able to remove itself', function(){
119                 var object = create();
121                 var methods = [];
123                 var one = function(){
124                         object.removeEvent('event', one);
125                         methods.push(1);
126                 };
127                 var two = function(){
128                         object.removeEvent('event', two);
129                         methods.push(2);
130                 };
131                 var three = function(){
132                         methods.push(3);
133                 };
135                 object.addEvent('event', one).addEvent('event', two).addEvent('event', three);
137                 object[fire]('event');
138                 expect(methods).toEqual([1, 2, 3]);
140                 object[fire]('event');
141                 expect(methods).toEqual([1, 2, 3, 3]);
142         });
146 var fragment = document.createDocumentFragment();
148 // Restore native fireEvent in IE for Syn
149 var createElement = function(tag, props){
150         var el = new Element(tag);
151         if (el._fireEvent) el.fireEvent = el._fireEvent;
152         return el.set(props);
155 describe('Element.Event', function(){
157         it('Should trigger the click event', function(){
159                 var callback = jasmine.createSpy('Element.Event click');
161                 var el = createElement('a', {
162                         text: 'test',
163                         styles: {
164                                 display: 'block',
165                                 overflow: 'hidden',
166                                 height: '1px'
167                         },
168                         events: {
169                                 click: callback
170                         }
171                 }).inject(document.body);
173                 Syn.trigger('click', null, el);
175                 expect(callback).toHaveBeenCalled();
176                 el.destroy();
177         });
179         it('Should trigger the click event and prevent the default behavior', function(){
181                 var callback = jasmine.createSpy('Element.Event click with prevent');
183                 var el = createElement('a', {
184                         text: 'test',
185                         styles: {
186                                 display: 'block',
187                                 overflow: 'hidden',
188                                 height: '1px'
189                         },
190                         events: {
191                                 click: function(event){
192                                         event.preventDefault();
193                                         callback();
194                                 }
195                         }
196                 }).inject(document.body);
198                 Syn.trigger('click', null, el);
200                 expect(callback).toHaveBeenCalled();
201                 el.destroy();
203         });
205         if (window.postMessage && !navigator.userAgent.match(/phantomjs/i)) it('Should trigger message event', function(){
207                 var theMessage, spy = jasmine.createSpy('message');
208                 window.addEvent('message', function(e){
209                         theMessage = e.event.data;
210                         spy();
211                 });
212                 window.postMessage('I am a message from outer space...', '*');
213                 waits(150);
214                 runs(function(){
215                         expect(spy).toHaveBeenCalled();
216                         expect(theMessage).toEqual('I am a message from outer space...');
217                 });
218         });
220         it('Should watch for a key-down event', function(){
222                 var callback = jasmine.createSpy('keydown');
224                 var div = createElement('div').addEvent('keydown', function(event){
225                         callback(event.key);
226                 }).inject(document.body);
228                 Syn.key('a', div);
230                 expect(callback).toHaveBeenCalledWith('a');
231                 div.destroy();
232         });
234         it('should clone events of an element', function(){
236                 var calls = 0;
238                 var element = new Element('div').addEvent('click', function(){ calls++; });
239                 element.fireEvent('click');
241                 expect(calls).toBe(1);
243                 var clone = new Element('div').cloneEvents(element, 'click');
244                 clone.fireEvent('click');
246                 expect(calls).toBe(2);
248                 element.addEvent('custom', function(){ calls += 2; }).fireEvent('custom');
250                 expect(calls).toBe(4);
252                 clone.cloneEvents(element);
253                 clone.fireEvent('click');
255                 expect(calls).toBe(5);
257                 clone.fireEvent('custom');
259                 expect(calls).toBe(7);
260         });
264 describe('Element.Event', function(){
265         // This is private API. Do not use.
267         it('should pass the name of the custom event to the callbacks', function(){
268                 var callbacks = 0;
269                 var callback = jasmine.createSpy('Element.Event custom');
271                 var fn = function(anything, type){
272                         expect(type).toEqual('customEvent');
273                         callbacks++;
274                 };
275                 Element.Events.customEvent = {
277                         base: 'click',
279                         condition: function(event, type){
280                                 fn(null, type);
281                                 return true;
282                         },
284                         onAdd: fn,
285                         onRemove: fn
287                 };
289                 var div = createElement('div').addEvent('customEvent', callback).inject(document.body);
291                 Syn.trigger('click', null, div);
293                 expect(callback).toHaveBeenCalled();
294                 div.removeEvent('customEvent', callback).destroy();
295                 expect(callbacks).toEqual(3);
296         });
300 describe('Element.Event.change', function(){
302         it('should not fire "change" for any property', function(){
303                 var callback = jasmine.createSpy('Element.Event.change');
305                 var radio = new Element('input', {
306                         'type': 'radio',
307                         'class': 'someClass',
308                         'checked': 'checked'
309                 }).addEvent('change', callback).inject(document.body);
311                 radio.removeClass('someClass');
312                 expect(callback).not.toHaveBeenCalled();
314                 var checkbox = new Element('input', {
315                         'type': 'checkbox',
316                         'class': 'someClass',
317                         'checked': 'checked'
318                 }).addEvent('change', callback).inject(document.body);
320                 checkbox.removeClass('someClass');
321                 expect(callback).not.toHaveBeenCalled();
323                 var text = new Element('input', {
324                         'type': 'text',
325                         'class': 'otherClass',
326                         'value': 'text value'
327                 }).addEvent('change', callback).inject(document.body);
329                 text.removeClass('otherClass');
330                 expect(callback).not.toHaveBeenCalled();
332                 [radio, checkbox, text].invoke('destroy');
333         });
337 describe('Element.Event keyup with f<key>', function(){
339         it('should pass event.key == f2 when pressing f2 on keyup and keydown', function(){
341                 var keydown = jasmine.createSpy('keydown');
342                 var keyup = jasmine.createSpy('keyup');
344                 var div = createElement('div')
345                         .addEvent('keydown', function(event){
346                                 keydown(event.key);
347                         })
348                         .addEvent('keyup', function(event){
349                                 keyup(event.key);
350                         })
351                         .inject(document.body);
353                 Syn.trigger('keydown', 'f2', div);
354                 Syn.trigger('keyup', 'f2', div);
356                 expect(keydown).toHaveBeenCalledWith('f2');
357                 expect(keyup).toHaveBeenCalledWith('f2');
359                 div.destroy();
361         });
365 describe('Keypress key code', function(){
367         /*<ltIE8>*/
368         // return early for IE8- because Syn.js does not fire events
369         if (!document.addEventListener) return;
370         /*</ltIE8>*/
372         var input, key, shift, done;
374         function keyHandler(e){
375                 key = e.key;
376                 shift = !!e.event.shiftKey;
377         }
379         function typeWriter(action){
380                 setTimeout(function () {
381                         Syn.type(action, 'keyTester');
382                 }, 1);
383                 if (done) return true;
384         }
386         beforeEach(function(){
387                 input = new Element('input', {
388                         'type': 'text',
389                         'id': 'keyTester'
390                 }).addEvent('keypress', keyHandler).inject(document.body);
391         });
393         afterEach(function(){
394                 input.removeEvent('keypress', keyHandler).destroy();
395                 input = key = shift = done = null;
396         });
398         it('should return "enter" in event.key', function(){
399                 typeWriter('[enter]');
400                 waits(50);
401                 runs(function(){
402                         expect(key).toBe('enter');
403                         expect(shift).not.toBeTruthy();
404                 });
405         });
407         it('should return "1" in event.key', function(){
408                 typeWriter('1');
409                 waits(50);
410                 runs(function(){
411                         expect(key).toBe('1');
412                         expect(shift).not.toBeTruthy();
413                 });
414         });
416         it('should return false when pressing SHIFT + 1', function(){
417                 typeWriter('[shift]![shift-up]');
418                 waits(50);
419                 runs(function(){
420                         expect(key).toBe(false);
421                         expect(shift).toBeTruthy();
422                 });
423         });
427 describe('Element.removeEvent', function(){
429         it('should remove the onunload method', function(){
430                 var text;
431                 var handler = function(){ text = 'nope'; };
432                 window.addEvent('unload', handler);
433                 window.removeEvent('unload', handler);
434                 window.fireEvent('unload');
435                 expect(text).toBe(undefined);
436         });
441 describe('Mouse wheel', function(){
443         function attachProperties(e, direction){
444                 e.detail = 1 * direction;
445                 e.wheelDelta = 1 * direction;
446                 e.deltaY = -1 * direction;
447         }
449         function dispatchFakeWheel(type, wheelDirection){
451                 var event;
452                 try {
453                         // Firefox
454                         event = document.createEvent("MouseEvents");
455                         event.initMouseEvent(type, true, true, window, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
456                         attachProperties(event, wheelDirection);
457                         window.dispatchEvent(event);
458                 } catch(e){}
460                 try {
461                         // Chrome, PhantomJS, Safari
462                         event = document.createEvent("WheelEvent");
463                         event.initMouseEvent(type, 0, 100, window, 0, 0, 0, 0, null, null, null, null);
464                         attachProperties(event, wheelDirection);
465                         window.dispatchEvent(event);
466                 } catch(e){}
468                 try {
469                         // IE9
470                         event = document.createEvent("HTMLEvents");
471                         event.initEvent(type, true, false);
472                         attachProperties(event, wheelDirection);
473                         window.dispatchEvent(event);
474                 } catch(e){}
476                 try {
477                         // IE10+, Safari
478                         var event = document.createEvent("MouseEvents");
479                         event.initEvent(type, true, true);
480                         attachProperties(event, wheelDirection);
481                         window.dispatchEvent(event);
482                 } catch(e){}
484                 try {
485                         // IE8
486                         var event = document.createEventObject();
487                         document.documentElement.fireEvent(type, event);
488                 } catch(e){}
489         }
491         var triggered = false;
492         var wheel = false;
493         var testWheel = !!window.addEventListener;
494         var callback = function(e){
495                 if (e.wheel) wheel = e.wheel > 0 ? 'wheel moved up' : 'wheel moved down';
496                 triggered = 'triggered';
497         };
499         beforeEach(function(){
500                 wheel = triggered = false;
501                 window.addEvent('mousewheel', callback);
502                 document.documentElement.addEvent('mousewheel', callback);
503         });
505         afterEach(function(){
506                 window.removeEvent('mousewheel', callback);
507                 document.documentElement.removeEvent('mousewheel', callback);
508         });
510         it('should trigger/listen to mousewheel event', function(){
511                 // http://jsfiddle.net/W6QrS/3
513                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(dispatchFakeWheel);
514                 expect(triggered).toBeTruthy();
515         });
517         it('should listen to mouse wheel direction', function(){
518                 // http://jsfiddle.net/58yCr/
520                 if (!testWheel) return;
522                 // fire event with wheel going up
523                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(function(type){
524                         dispatchFakeWheel(type, 120);
525                 });
526                 expect(wheel).toEqual('wheel moved up');
527                 wheel = false;
529                 // fire event with wheel going down
530                 ['mousewheel', 'wheel' ,'DOMMouseScroll' ].each(function(type){
531                         dispatchFakeWheel(type, -120);
532                 });
533                 expect(wheel).toEqual('wheel moved down');
534         });
537 })();