11 var Local = Local || {};
13 var fire = 'fireEvent',
15 return new Element('div');
18 describe('Events API: Element', function(){
20 beforeEach(function(){
22 Local.fn = function(){
23 return Local.called++;
27 it('should add an Event to the Class', function(){
28 var object = create();
30 object.addEvent('event', Local.fn)[fire]('event');
32 expect(Local.called).to.equal(1);
35 it('should add multiple Events to the Class', function(){
39 })[fire]('event1')[fire]('event2');
41 expect(Local.called).to.equal(2);
44 it('should remove a specific method for an event', function(){
45 var object = create();
46 var x = 0, fn = function(){ x++; };
48 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvent('event', Local.fn)[fire]('event');
50 expect(x).to.equal(1);
51 expect(Local.called).to.equal(0);
54 it('should remove an event and its methods', function(){
55 var object = create();
56 var x = 0, fn = function(){ x++; };
58 object.addEvent('event', Local.fn).addEvent('event', fn).removeEvents('event')[fire]('event');
60 expect(x).to.equal(0);
61 expect(Local.called).to.equal(0);
64 it('should remove all events', function(){
65 var object = create();
66 var x = 0, fn = function(){ x++; };
68 object.addEvent('event1', Local.fn).addEvent('event2', fn).removeEvents();
69 object[fire]('event1')[fire]('event2');
72 object.removeEvents()[fire]('event1')[fire]('event2');
74 expect(x).to.equal(0);
75 expect(Local.called).to.equal(0);
78 it('should remove events with an object', function(){
79 var object = create();
85 object.addEvent('event1', function(){ Local.fn(); }).addEvents(events)[fire]('event1');
86 expect(Local.called).to.equal(2);
88 object.removeEvents(events);
89 object[fire]('event1');
90 expect(Local.called).to.equal(3);
92 object[fire]('event2');
93 expect(Local.called).to.equal(3);
96 it('should remove an event immediately', function(){
97 var object = create();
101 var three = function(){
105 object.addEvent('event', function(){
107 this.removeEvent('event', three);
108 }).addEvent('event', function(){
110 }).addEvent('event', three);
112 object[fire]('event');
113 expect(methods).to.eql([1, 2]);
115 object[fire]('event');
116 expect(methods).to.eql([1, 2, 1, 2]);
119 it('should be able to remove itself', function(){
120 var object = create();
124 var one = function(){
125 object.removeEvent('event', one);
128 var two = function(){
129 object.removeEvent('event', two);
132 var three = function(){
136 object.addEvent('event', one).addEvent('event', two).addEvent('event', three);
138 object[fire]('event');
139 expect(methods).to.eql([1, 2, 3]);
141 object[fire]('event');
142 expect(methods).to.eql([1, 2, 3, 3]);
147 // Restore native fireEvent in IE for Syn.
148 var createElement = function(tag, props){
149 var el = new Element(tag);
150 if (el._fireEvent) el.fireEvent = el._fireEvent;
151 return el.set(props);
154 describe('Element.Event', function(){
156 it('Should trigger the click event', function(){
157 var callback = sinon.spy();
159 var el = createElement('a', {
169 }).inject(document.body);
171 syn.trigger(el, 'click');
173 expect(callback.called).to.equal(true);
177 it('Should trigger the click event and prevent the default behavior', function(){
178 var callback = sinon.spy();
180 var el = createElement('a', {
188 click: function(event){
189 event.preventDefault();
193 }).inject(document.body);
195 syn.trigger(el, 'click');
197 expect(callback.called).to.equal(true);
201 var ddescribe = (window.postMessage && !navigator.userAgent.match(/phantomjs/i)) ? describe : xdescribe;
202 ddescribe('(async)', function(){
204 beforeEach(function(done){
206 this.spy = sinon.spy();
208 window.addEvent('message', function(e){
209 self.message = e.event.data;
212 window.postMessage('I am a message from outer space...', '*');
213 setTimeout(done, 150);
216 it('Should trigger a message event', function(){
217 expect(this.spy.called).to.equal(true);
218 expect(this.message).to.equal('I am a message from outer space...');
223 it('Should watch for a key-down event', function(){
224 var callback = sinon.spy();
226 var div = createElement('div').addEvent('keydown', function(event){
228 }).inject(document.body);
232 expect(callback.calledWith('a')).to.equal(true);
236 it('should clone events of an element', function(){
239 var element = new Element('div').addEvent('click', function(){ calls++; });
240 element.fireEvent('click');
242 expect(calls).to.equal(1);
244 var clone = new Element('div').cloneEvents(element, 'click');
245 clone.fireEvent('click');
247 expect(calls).to.equal(2);
249 element.addEvent('custom', function(){ calls += 2; }).fireEvent('custom');
251 expect(calls).to.equal(4);
253 clone.cloneEvents(element);
254 clone.fireEvent('click');
256 expect(calls).to.equal(5);
258 clone.fireEvent('custom');
260 expect(calls).to.equal(7);
265 describe('Element.Event', function(){
266 // This is private API. Do not use.
268 it('should pass the name of the custom event to the callbacks', function(){
270 var callback = sinon.spy();
272 var fn = function(anything, type){
273 expect(type).to.equal('customEvent');
276 Element.Events.customEvent = {
280 condition: function(event, type){
290 var div = createElement('div').addEvent('customEvent', callback).inject(document.body);
292 syn.trigger(div, 'click');
294 expect(callback.called).to.equal(true);
295 div.removeEvent('customEvent', callback).destroy();
296 expect(callbacks).to.equal(3);
301 describe('Element.Event.change', function(){
303 it('should not fire "change" for any property', function(){
304 var callback = sinon.spy();
306 var radio = new Element('input', {
308 'class': 'someClass',
310 }).addEvent('change', callback).inject(document.body);
312 radio.removeClass('someClass');
313 expect(callback.called).to.equal(false);
315 var checkbox = new Element('input', {
317 'class': 'someClass',
319 }).addEvent('change', callback).inject(document.body);
321 checkbox.removeClass('someClass');
322 expect(callback.called).to.equal(false);
324 var text = new Element('input', {
326 'class': 'otherClass',
327 'value': 'text value'
328 }).addEvent('change', callback).inject(document.body);
330 text.removeClass('otherClass');
331 expect(callback.called).to.equal(false);
333 [radio, checkbox, text].invoke('destroy');
338 describe('Element.Event keyup with f<key>', function(){
340 it('should pass event.key == f2 when pressing f2 on keyup and keydown', function(){
341 var keydown = sinon.spy();
342 var keyup = sinon.spy();
344 var div = createElement('div')
345 .addEvent('keydown', function(event){
348 .addEvent('keyup', function(event){
351 .inject(document.body);
353 syn.trigger(div, 'keydown', 'f2');
354 syn.trigger(div, 'keyup', 'f2');
356 expect(keydown.calledWith('f2')).to.equal(true);
357 expect(keyup.calledWith('f2')).to.equal(true);
364 describe('Keypress key code', function(){
367 // Return early for IE8- because Syn.js does not fire events.
368 if (!document.addEventListener) return;
371 var _input, _key, _shift;
372 DOMEvent.defineKey(33, 'pageup');
374 function keyHandler(e){
376 _shift = !!e.event.shiftKey;
379 function typeWriter(action, cb){
380 _input = new Element('input', {
383 }).addEvent('keypress', keyHandler).inject(document.body);
385 setTimeout(function(){
386 syn.type('keyTester', action);
387 setTimeout(function(){
393 afterEach(function(){
394 _input.removeEvent('keypress', keyHandler).destroy();
397 it('should return "enter" in event.key', function(done){
398 typeWriter('[enter]', function(key, shift){
399 expect(key).to.equal('enter');
400 expect(shift).to.equal(false);
405 it('should return "1" in event.key', function(done){
406 typeWriter('1', function(key, shift){
407 expect(key).to.equal('1');
408 expect(shift).to.equal(false);
413 it('should return "!" when pressing SHIFT + 1', function(done){
414 typeWriter('[shift]![shift-up]', function(key, shift){
415 expect(key).to.equal('!');
416 expect(shift).to.equal(true);
421 it('should map code 33 correctly with keypress event', function(){
422 var mock = {type: 'keypress', which: 33, shiftKey: true};
423 var e = new DOMEvent(mock);
424 expect(e.key).to.equal('!');
429 describe('Element.removeEvent', function(){
431 it('should remove the onunload method', function(){
433 var handler = function(){ text = 'nope'; };
434 window.addEvent('unload', handler);
435 window.removeEvent('unload', handler);
436 window.fireEvent('unload');
437 expect(text).to.equal(undefined);
442 describe('relatedTarget', function(){
444 var outer = new Element('div');
445 new Element('div').inject(outer);
446 ['mouseenter', 'mouseleave', 'mouseover', 'mouseout'].each(function(event, i){
447 it('should listen to a ' + event + ' event and set the correct relatedTarget', function(){
448 var mockEvent = {type: event};
449 mockEvent[(i % 2 == 0 ? 'from' : 'to') + 'Element'] = outer; // Simulate FF that does not set relatedTarget.
451 var e = new DOMEvent(mockEvent);
452 expect(e.type).to.equal(event);
453 expect(e.relatedTarget).to.equal(outer);
459 describe('Mouse wheel', function(){
461 function attachProperties(e, direction){
462 e.detail = 1 * direction;
463 e.wheelDelta = 1 * direction;
464 e.deltaY = -1 * direction;
467 function dispatchFakeWheel(type, wheelDirection){
472 event = document.createEvent('MouseEvents');
473 event.initMouseEvent(type, true, true, window, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
474 attachProperties(event, wheelDirection);
475 window.dispatchEvent(event);
479 // Chrome, PhantomJS, Safari
480 event = document.createEvent('WheelEvent');
481 event.initMouseEvent(type, 0, 100, window, 0, 0, 0, 0, null, null, null, null);
482 attachProperties(event, wheelDirection);
483 window.dispatchEvent(event);
488 event = document.createEvent('HTMLEvents');
489 event.initEvent(type, true, false);
490 attachProperties(event, wheelDirection);
491 window.dispatchEvent(event);
496 event = document.createEvent('MouseEvents');
497 event.initEvent(type, true, true);
498 attachProperties(event, wheelDirection);
499 window.dispatchEvent(event);
504 event = document.createEventObject();
505 document.documentElement.fireEvent(type, event);
509 var triggered = false;
511 var testWheel = !!window.addEventListener;
512 var callback = function(e){
513 if (e.wheel) wheel = e.wheel > 0 ? 'wheel moved up' : 'wheel moved down';
517 beforeEach(function(){
518 wheel = triggered = false;
519 window.addEvent('mousewheel', callback);
520 document.documentElement.addEvent('mousewheel', callback);
523 afterEach(function(){
524 window.removeEvent('mousewheel', callback);
525 document.documentElement.removeEvent('mousewheel', callback);
528 it('should trigger/listen to mousewheel event', function(){
529 // http://jsfiddle.net/W6QrS/3
531 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(dispatchFakeWheel);
532 expect(triggered).to.equal(true);
535 it('should listen to mouse wheel direction', function(){
536 // http://jsfiddle.net/58yCr/
538 if (!testWheel) return;
540 // Fire event with wheel going up.
541 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(function(type){
542 dispatchFakeWheel(type, 120);
544 expect(wheel).to.equal('wheel moved up');
547 // Fire event with wheel going down.
548 ['mousewheel', 'wheel', 'DOMMouseScroll' ].each(function(type){
549 dispatchFakeWheel(type, -120);
551 expect(wheel).to.equal('wheel moved down');