1 use webcore::value::{Reference, Value};
2 use webcore::try_from::{TryFrom, TryInto};
3 use webapi::event_target::EventTarget;
4 use webapi::window::Window;
5
6 /// The `IEvent` interface represents any event which takes place in the DOM; some
7 /// are user-generated (such as mouse or keyboard events), while others are
8 /// generated by APIs (such as events that indicate an animation has finished
9 /// running, a video has been paused, and so forth). There are many types of event,
10 /// some of which use other interfaces based on the main `IEvent` interface. `IEvent`
11 /// itself contains the properties and methods which are common to all events.
12 ///
13 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event)
14 pub trait IEvent: AsRef< Reference > + TryFrom< Value > {
15 /// Indicates whether this event bubbles upward through the DOM.
16 ///
17 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event)
18 #[inline]
bubbles( &self ) -> bool19 fn bubbles( &self ) -> bool {
20 js!(
21 return @{self.as_ref()}.bubbles;
22 ).try_into().unwrap()
23 }
24
25 /// A historical alias to `Event.stopPropagation()`.
26 ///
27 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelBubble)
28 #[inline]
cancel_bubble( &self ) -> bool29 fn cancel_bubble( &self ) -> bool {
30 js!(
31 return @{self.as_ref()}.cancelBubble;
32 ).try_into().unwrap()
33 }
34
35 /// A historical alias to `Event.stopPropagation()`.
36 /// Setting this to `true` before returning from an event handler will stop propagation
37 /// of the event.
38 ///
39 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelBubble)
40 #[inline]
set_cancel_bubble( &self, value: bool )41 fn set_cancel_bubble( &self, value: bool ) {
42 js! { @(no_return)
43 @{self.as_ref()}.cancelBubble = @{value};
44 }
45 }
46
47 /// Indicates whether the event is cancelable.
48 ///
49 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelable)
50 #[inline]
cancelable( &self ) -> bool51 fn cancelable( &self ) -> bool {
52 js!(
53 return @{self.as_ref()}.cancelable;
54 ).try_into().unwrap()
55 }
56
57 /// A reference to the currently registered target of this event.
58 ///
59 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget)
60 #[inline]
current_target( &self ) -> Option< EventTarget >61 fn current_target( &self ) -> Option< EventTarget > {
62 js!(
63 return @{self.as_ref()}.currentTarget;
64 ).try_into().ok()
65 }
66
67 /// Indicates whether `preventDefault` has been called on this event.
68 ///
69 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/defaultPrevented)
70 #[inline]
default_prevented( &self ) -> bool71 fn default_prevented( &self ) -> bool {
72 js!(
73 return @{self.as_ref()}.defaultPrevented;
74 ).try_into().unwrap()
75 }
76
77 /// Indicates which phase of event flow is currently being evaluated.
78 ///
79 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase)
event_phase( &self ) -> EventPhase80 fn event_phase( &self ) -> EventPhase {
81 match js!(
82 return @{self.as_ref()}.eventPhase;
83 ).try_into().unwrap() {
84 0 => EventPhase::None,
85 1 => EventPhase::Capturing,
86 2 => EventPhase::AtTarget,
87 3 => EventPhase::Bubbling,
88 _ => unreachable!("Unexpected EventPhase type"),
89 }
90 }
91
92 /// Prevents any further listeners from being called for this event.
93 ///
94 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation)
95 #[inline]
stop_immediate_propagation( &self )96 fn stop_immediate_propagation( &self ) {
97 js! { @(no_return)
98 @{self.as_ref()}.stopImmediatePropagation();
99 }
100 }
101
102 /// Stops the propagation of this event to descendants in the DOM.
103 ///
104 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)
105 #[inline]
stop_propagation( &self )106 fn stop_propagation( &self ) {
107 js! { @(no_return)
108 @{self.as_ref()}.stopPropagation();
109 }
110 }
111
112
113 /// Returns a reference to the target to which this event was originally registered.
114 ///
115 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)
116 #[inline]
target( &self ) -> Option< EventTarget >117 fn target( &self ) -> Option< EventTarget > {
118 js!(
119 return @{self.as_ref()}.target;
120 ).try_into().ok()
121 }
122
123 /// Returns the time in milliseconds at which this event was created.
124 ///
125 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp)
126 #[inline]
time_stamp( &self ) -> Option< f64 >127 fn time_stamp( &self ) -> Option< f64 > {
128 js!(
129 return @{self.as_ref()}.timeStamp;
130 ).try_into().ok()
131 }
132
133 /// Indicates whether the event was generated by a user action.
134 #[inline]
is_trusted( &self ) -> bool135 fn is_trusted( &self ) -> bool {
136 js!(
137 return @{self.as_ref()}.isTrusted;
138 ).try_into().unwrap()
139 }
140
141 /// Returns a string containing the type of event. It is set when
142 /// the event is constructed and is the name commonly used to refer
143 /// to the specific event.
144 ///
145 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/type)
146 #[inline]
event_type( &self ) -> String147 fn event_type( &self ) -> String {
148 js!(
149 return @{self.as_ref()}.type;
150 ).try_into().unwrap()
151 }
152
153 /// Cancels the event if it is cancelable, without
154 /// stopping further propagation of the event.
155 ///
156 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
157 #[inline]
prevent_default( &self )158 fn prevent_default( &self ) {
159 js! { @(no_return)
160 @{self.as_ref()}.preventDefault();
161 }
162 }
163 }
164
165 /// Indicates the phase of event flow during event proessing.
166 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
167 pub enum EventPhase {
168 /// No event is currently being processed.
169 None,
170 /// The event is being propagated down through the target's ancestors.
171 Capturing,
172 /// The target is currently processing the event.
173 AtTarget,
174 /// The event is propagating back up through the target's ancestors.
175 Bubbling,
176 }
177
178 pub trait ConcreteEvent: IEvent {
179 /// A string representing the event type.
180 ///
181 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event/type)
182 const EVENT_TYPE: &'static str;
183 }
184
185 /// A reference to a JavaScript object which implements the [IEvent](trait.IEvent.html)
186 /// interface.
187 ///
188 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Event)
189 pub struct Event( Reference );
190
191 impl IEvent for Event {}
192
193 reference_boilerplate! {
194 Event,
195 instanceof Event
196 }
197
198 /// The `ChangeEvent` is fired for input, select, and textarea
199 /// elements when a change to the element's value is committed
200 /// by the user. Unlike the input event, the change event is not
201 /// necessarily fired for each change to an element's value.
202 ///
203 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/change)
204 pub struct ChangeEvent( Reference );
205
206 impl IEvent for ChangeEvent {}
207 impl ConcreteEvent for ChangeEvent {
208 const EVENT_TYPE: &'static str = "change";
209 }
210
211 reference_boilerplate! {
212 ChangeEvent,
213 instanceof Event
214 convertible to Event
215 }
216
217 /// The `IUiEvent` interface represents simple user interface events.
218 ///
219 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent)
220 pub trait IUiEvent: IEvent {
221 /// Provides the current click count for this event, if applicable.
222 ///
223 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail)
224 #[inline]
detail( &self ) -> i32225 fn detail( &self ) -> i32 {
226 js!(
227 return @{self.as_ref()}.detail;
228 ).try_into().unwrap()
229 }
230
231 /// Returns the `WindowProxy` that generated the event.
232 ///
233 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view)
234 #[inline]
view( &self ) -> Option< Window >235 fn view( &self ) -> Option< Window > {
236 js!(
237 return @{self.as_ref()}.view;
238 ).try_into().ok()
239 }
240 }
241
242 /// A reference to a JavaScript object which implements the [IUiEvent](trait.IUiEvent.html)
243 /// interface.
244 ///
245 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent)
246 pub struct UiEvent( Reference );
247
248 impl IEvent for UiEvent {}
249 impl IUiEvent for UiEvent {}
250
251 reference_boilerplate! {
252 UiEvent,
253 instanceof UIEvent
254 convertible to Event
255 }
256
257 /// The `LoadEvent` is fired when a resource and its dependent resources have finished loading.
258 ///
259 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/load)
260 pub struct LoadEvent( Reference );
261
262 impl IEvent for LoadEvent {}
263 impl IUiEvent for LoadEvent {}
264 impl ConcreteEvent for LoadEvent {
265 const EVENT_TYPE: &'static str = "load";
266 }
267
268 reference_boilerplate! {
269 LoadEvent,
270 instanceof UiEvent
271 convertible to Event
272 convertible to UiEvent
273 }
274
275 /// The `IMouseEvent` interface represents events that occur due to the user
276 /// interacting with a pointing device (such as a mouse).
277 ///
278 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
279 pub trait IMouseEvent: IUiEvent {
280 /// Returns whether the Alt key was down when this event was fired.
281 ///
282 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey)
283 #[inline]
alt_key( &self ) -> bool284 fn alt_key( &self ) -> bool {
285 js!(
286 return @{self.as_ref()}.altKey;
287 ).try_into().unwrap()
288 }
289
290 /// Indicates the mouse button that fired this event.
291 ///
292 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)
button( &self ) -> MouseButton293 fn button( &self ) -> MouseButton {
294 match js!(
295 return @{self.as_ref()}.button;
296 ).try_into().unwrap() {
297 0 => MouseButton::Left,
298 1 => MouseButton::Wheel,
299 2 => MouseButton::Right,
300 3 => MouseButton::Button4,
301 4 => MouseButton::Button5,
302 _ => unreachable!("Unexpected MouseEvent.button value"),
303 }
304 }
305
306 /// Indicates which mouse buttons were down when this event was fired.
307 ///
308 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)
buttons( &self ) -> MouseButtonsState309 fn buttons( &self ) -> MouseButtonsState {
310 MouseButtonsState(
311 js!(
312 return @{self.as_ref()}.buttons;
313 ).try_into().unwrap()
314 )
315 }
316
317 /// Returns the X position in the application's client area where this event occured.
318 ///
319 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)
320 #[inline]
client_x( &self ) -> f64321 fn client_x( &self ) -> f64 {
322 js!(
323 return @{self.as_ref()}.clientX;
324 ).try_into().unwrap()
325 }
326
327 /// Returns the Y position in the application's client area where this event occured.
328 ///
329 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY)
330 #[inline]
client_y( &self ) -> f64331 fn client_y( &self ) -> f64 {
332 js!(
333 return @{self.as_ref()}.clientY;
334 ).try_into().unwrap()
335 }
336
337 /// Indicates whether the Ctrl key was down when this event fired.
338 ///
339 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey)
340 #[inline]
ctrl_key( &self ) -> bool341 fn ctrl_key( &self ) -> bool {
342 js!(
343 return @{self.as_ref()}.ctrlKey;
344 ).try_into().unwrap()
345 }
346
347 /// Returns the current state of the specified modifier key.
348 ///
349 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/getModifierState)
350 #[inline]
get_modifier_state( &self, key: ModifierKey ) -> bool351 fn get_modifier_state( &self, key: ModifierKey ) -> bool {
352 get_event_modifier_state( self, key )
353 }
354
355 /// Indicates whether the Meta key was down when this event fired.
356 ///
357 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/metaKey)
358 #[inline]
meta_key( &self ) -> bool359 fn meta_key( &self ) -> bool {
360 js!(
361 return @{self.as_ref()}.metaKey;
362 ).try_into().unwrap()
363 }
364
365 /// Returns the change in X coordinate of the pointer between this event and the previous
366 /// MouseMove event.
367 ///
368 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)
369 #[inline]
movement_x( &self ) -> f64370 fn movement_x( &self ) -> f64 {
371 js!(
372 return @{self.as_ref()}.movementX;
373 ).try_into().unwrap()
374 }
375
376 /// Returns the change in Y coordinate of the pointer between this event and the previous
377 /// MouseMove event.
378 ///
379 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX)
380 #[inline]
movement_y( &self ) -> f64381 fn movement_y( &self ) -> f64 {
382 js!(
383 return @{self.as_ref()}.movementY;
384 ).try_into().unwrap()
385 }
386
387 /// Returns the ID of the hit region affected by the event.
388 ///
389 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/region)
390 #[inline]
region( &self ) -> Option< String >391 fn region( &self ) -> Option< String > {
392 js!(
393 return @{self.as_ref()}.region;
394 ).try_into().ok()
395 }
396
397 /// Returns the secondary target of this event, if any.
398 ///
399 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget)
400 #[inline]
related_target( &self ) -> Option< EventTarget >401 fn related_target( &self ) -> Option< EventTarget > {
402 js!(
403 return @{self.as_ref()}.relatedTarget;
404 ).try_into().ok()
405 }
406
407 /// Returns the X position of the pointer in screen coordinates.
408 ///
409 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)
410 #[inline]
screen_x( &self ) -> f64411 fn screen_x( &self ) -> f64 {
412 js!(
413 return @{self.as_ref()}.screenX;
414 ).try_into().unwrap()
415 }
416
417 /// Returns the Y position of the pointer in screen coordinates.
418 ///
419 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY)
420 #[inline]
screen_y( &self ) -> f64421 fn screen_y( &self ) -> f64 {
422 js!(
423 return @{self.as_ref()}.screenY;
424 ).try_into().unwrap()
425 }
426
427 /// Indicates whether the Shift key was down when this event was fired.
428 ///
429 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey)
430 #[inline]
shift_key( &self ) -> bool431 fn shift_key( &self ) -> bool {
432 js!(
433 return @{self.as_ref()}.shiftKey;
434 ).try_into().unwrap()
435 }
436 }
437
438 /// Represents buttons on a mouse during mouse events.
439 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
440 pub enum MouseButton {
441 /// The left mouse button.
442 Left,
443 /// The mouse wheel/middle mouse button.
444 Wheel,
445 /// The right mouse button.
446 Right,
447 /// The fourth mouse button (browser back).
448 Button4,
449 /// The fifth mouse button (browser forward).
450 Button5,
451 }
452
453 /// Represents the state of mouse buttons in a `MouseEvent`.
454 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
455 pub struct MouseButtonsState(u8);
456
457 impl MouseButtonsState {
is_down(&self, button: MouseButton) -> bool458 pub fn is_down(&self, button: MouseButton) -> bool {
459 match button {
460 MouseButton::Left => self.0 & 0b1 != 0,
461 MouseButton::Right => self.0 & 0b10 != 0,
462 MouseButton::Wheel => self.0 & 0b100 != 0,
463 MouseButton::Button4 => self.0 & 0b1000 != 0,
464 MouseButton::Button5 => self.0 & 0b1_0000 != 0,
465 }
466 }
467 }
468
469 /// A reference to a JavaScript object which implements the [IMouseEvent](trait.IMouseEvent.html)
470 /// interface.
471 ///
472 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
473 pub struct MouseEvent( Reference );
474
475 impl IEvent for MouseEvent {}
476 impl IUiEvent for MouseEvent {}
477 impl IMouseEvent for MouseEvent {}
478
479 reference_boilerplate! {
480 MouseEvent,
481 instanceof MouseEvent
482 convertible to Event
483 convertible to UiEvent
484 }
485
486 /// The `ClickEvent` is fired when a pointing device button (usually a
487 /// mouse's primary button) is pressed and released on a single element.
488 ///
489 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/click)
490 pub struct ClickEvent( Reference );
491
492 impl IEvent for ClickEvent {}
493 impl IUiEvent for ClickEvent {}
494 impl IMouseEvent for ClickEvent {}
495 impl ConcreteEvent for ClickEvent {
496 const EVENT_TYPE: &'static str = "click";
497 }
498
499 reference_boilerplate! {
500 ClickEvent,
501 instanceof MouseEvent
502 convertible to Event
503 convertible to UiEvent
504 convertible to MouseEvent
505 }
506
507 /// The `DoubleClickEvent` is fired when a pointing device button
508 /// (usually a mouse's primary button) is clicked twice on a single
509 /// element.
510 ///
511 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/dblclick)
512 pub struct DoubleClickEvent( Reference );
513
514 impl IEvent for DoubleClickEvent {}
515 impl IUiEvent for DoubleClickEvent {}
516 impl IMouseEvent for DoubleClickEvent {}
517 impl ConcreteEvent for DoubleClickEvent {
518 const EVENT_TYPE: &'static str = "dblclick";
519 }
520
521 reference_boilerplate! {
522 DoubleClickEvent,
523 instanceof MouseEvent
524 convertible to Event
525 convertible to UiEvent
526 convertible to MouseEvent
527 }
528
529 /// `IKeyboardEvent` objects describe a user interaction with the
530 /// keyboard. Each event describes a key; the event type identifies
531 /// what kind of activity was performed.
532 ///
533 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
534 pub trait IKeyboardEvent: IEvent {
535 /// Indicates whether the Alt key was down when this event was fired.
536 ///
537 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/altKey)
538 #[inline]
alt_key( &self ) -> bool539 fn alt_key( &self ) -> bool {
540 js!(
541 return @{self.as_ref()}.altKey;
542 ).try_into().unwrap()
543 }
544
545 /// Returns a code value that indicates the physical key pressed on the keyboard.
546 ///
547 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
548 #[inline]
code( &self ) -> String549 fn code( &self ) -> String {
550 js!(
551 return @{self.as_ref()}.code;
552 ).try_into().unwrap()
553 }
554
555 /// Returns whether the Ctrl key was down when this event was fired.
556 ///
557 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/ctrlKey)
558 #[inline]
ctrl_key( &self ) -> bool559 fn ctrl_key( &self ) -> bool {
560 js!(
561 return @{self.as_ref()}.ctrlKey;
562 ).try_into().unwrap()
563 }
564
565
566 /// Returns whether a modifier key was down when this event was fired.
567 ///
568 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState)
569 #[inline]
get_modifier_state( &self, key: ModifierKey ) -> bool570 fn get_modifier_state( &self, key: ModifierKey ) -> bool {
571 get_event_modifier_state( self, key )
572 }
573
574 /// Returns whether this event was fired during composition.
575 ///
576 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/isComposing)
577 #[inline]
is_composing( &self ) -> bool578 fn is_composing( &self ) -> bool {
579 js!(
580 return @{self.as_ref()}.isComposing;
581 ).try_into().unwrap()
582 }
583
584 /// Returns the location of the key on the keyboard.
585 ///
586 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location)
location( &self ) -> KeyboardLocation587 fn location( &self ) -> KeyboardLocation {
588 match js!(
589 return @{self.as_ref()}.location;
590 ).try_into().unwrap() {
591 0 => KeyboardLocation::Standard,
592 1 => KeyboardLocation::Left,
593 2 => KeyboardLocation::Right,
594 3 => KeyboardLocation::Numpad,
595 4 => KeyboardLocation::Mobile,
596 5 => KeyboardLocation::Joystick,
597 _ => unreachable!("Unexpected KeyboardEvent.location value"),
598 }
599 }
600
601 /// Returns the value of a key or keys pressed by the user.
602 ///
603 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
604 #[inline]
key( &self ) -> String605 fn key( &self ) -> String {
606 js!(
607 return @{self.as_ref()}.key;
608 ).into_string().unwrap()
609 }
610
611 /// Indicates whether the Meta key was down when this event was fired.
612 ///
613 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey)
614 #[inline]
meta_key( &self ) -> bool615 fn meta_key( &self ) -> bool {
616 js!(
617 return @{self.as_ref()}.metaKey;
618 ).try_into().unwrap()
619 }
620
621 /// Indicates whether the key is held down such that it is repeating.
622 ///
623 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat)
624 #[inline]
repeat( &self ) -> bool625 fn repeat( &self ) -> bool {
626 js!(
627 return @{self.as_ref()}.repeat;
628 ).try_into().unwrap()
629 }
630
631 /// Indicates whether the Shift key was down when this event was fired.
632 ///
633 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/shiftKey)
634 #[inline]
shift_key( &self ) -> bool635 fn shift_key( &self ) -> bool {
636 js!(
637 return @{self.as_ref()}.shiftKey;
638 ).try_into().unwrap()
639 }
640 }
641
642 /// A modifier key on the keyboard.
643 #[allow(missing_docs)]
644 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
645 pub enum ModifierKey {
646 Alt,
647 AltGr,
648 CapsLock,
649 Ctrl,
650 Function,
651 FunctionLock,
652 Hyper,
653 Meta,
654 NumLock,
655 OS,
656 ScrollLock,
657 Shift,
658 Super,
659 Symbol,
660 SymbolLock,
661 }
662
663 /// Used by KeyboardEvent and MouseEvent to get the state of a modifier key.
get_event_modifier_state< T: IEvent >( event: &T, key: ModifierKey ) -> bool664 fn get_event_modifier_state< T: IEvent >( event: &T, key: ModifierKey ) -> bool {
665 js!(
666 return @{event.as_ref()}.getModifierState( @{
667 match key {
668 ModifierKey::Alt => "Alt",
669 ModifierKey::AltGr => "AltGraph",
670 ModifierKey::CapsLock => "CapsLock",
671 ModifierKey::Ctrl => "Control",
672 ModifierKey::Function => "Fn",
673 ModifierKey::FunctionLock => "FnLock",
674 ModifierKey::Hyper => "Hyper",
675 ModifierKey::Meta => "Meta",
676 ModifierKey::NumLock => "NumLock",
677 ModifierKey::OS => "OS",
678 ModifierKey::ScrollLock => "ScrollLock",
679 ModifierKey::Shift => "Shift",
680 ModifierKey::Super => "Super",
681 ModifierKey::Symbol => "Symbol",
682 ModifierKey::SymbolLock => "SymbolLock",
683 }
684 } );
685 ).try_into().unwrap()
686 }
687
688 /// The location on the keyboard of a key.
689 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
690 pub enum KeyboardLocation {
691 /// The key has only one version, or the location can't be distinguished.
692 Standard,
693 /// The left-hand version of a key.
694 Left,
695 /// The right-hand version of a key.
696 Right,
697 /// The key was on a numeric pad.
698 Numpad,
699 /// The key was on a mobile device.
700 Mobile,
701 /// The key was on a joystick.
702 Joystick,
703 }
704
705 /// A reference to a JavaScript object which implements the [IKeyboardEvent](trait.IKeyboardEvent.html)
706 /// interface.
707 ///
708 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
709 pub struct KeyboardEvent( Reference );
710
711 impl IEvent for KeyboardEvent {}
712 impl IKeyboardEvent for KeyboardEvent {}
713
714 reference_boilerplate! {
715 KeyboardEvent,
716 instanceof KeyboardEvent
717 convertible to Event
718 }
719
720 /// The `KeypressEvent` is fired when a key is pressed down, and that
721 /// key normally produces a character value.
722 ///
723 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/keypress)
724 pub struct KeypressEvent( Reference );
725
726 impl IEvent for KeypressEvent {}
727 impl IKeyboardEvent for KeypressEvent {}
728 impl ConcreteEvent for KeypressEvent {
729 const EVENT_TYPE: &'static str = "keypress";
730 }
731
732 reference_boilerplate! {
733 KeypressEvent,
734 instanceof KeyboardEvent
735 convertible to Event
736 convertible to KeyboardEvent
737 }
738
739 /// The `IFocusEvent` interface represents focus-related
740 /// events.
741 ///
742 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent)
743 pub trait IFocusEvent: IEvent {
744 /// Returns the secondary target of this event, if any.
745 ///
746 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
747 #[inline]
related_target( &self ) -> Option< EventTarget >748 fn related_target( &self ) -> Option< EventTarget > {
749 js!(
750 return @{self.as_ref()}.relatedTarget;
751 ).try_into().ok()
752 }
753 }
754
755 /// A reference to a JavaScript object which implements the [IFocusEvent](trait.IFocusEvent.html)
756 /// interface.
757 ///
758 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent)
759 pub struct FocusRelatedEvent( Reference );
760
761 impl IEvent for FocusRelatedEvent {}
762 impl IFocusEvent for FocusRelatedEvent {}
763
764 reference_boilerplate! {
765 FocusRelatedEvent,
766 instanceof FocusEvent
767 convertible to Event
768 }
769
770 /// The `FocusEvent` is fired when an element has received focus. The main
771 /// difference between this event and focusin is that only the latter bubbles.
772 ///
773 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/focus)
774 pub struct FocusEvent( Reference );
775
776 impl IEvent for FocusEvent {}
777 impl IFocusEvent for FocusEvent {}
778 impl ConcreteEvent for FocusEvent {
779 const EVENT_TYPE: &'static str = "focus";
780 }
781
782 reference_boilerplate! {
783 FocusEvent,
784 instanceof FocusEvent
785 convertible to Event
786 convertible to FocusRelatedEvent
787 }
788
789 /// The `BlurEvent` is fired when an element has lost focus. The main difference
790 /// between this event and focusout is that only the latter bubbles.
791 ///
792 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/blur)
793 pub struct BlurEvent( Reference );
794
795 impl IEvent for BlurEvent {}
796 impl IFocusEvent for BlurEvent {}
797 impl ConcreteEvent for BlurEvent {
798 const EVENT_TYPE: &'static str = "blur";
799 }
800
801 reference_boilerplate! {
802 BlurEvent,
803 instanceof FocusEvent
804 convertible to Event
805 convertible to FocusRelatedEvent
806 }
807
808 /// The `HashChangeEvent` is fired when the fragment
809 /// identifier of the URL has changed (the part of the URL
810 /// that follows the # symbol, including the # symbol).
811 ///
812 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/hashchange)
813 pub struct HashChangeEvent( Reference );
814
815 impl IEvent for HashChangeEvent {}
816 impl ConcreteEvent for HashChangeEvent {
817 const EVENT_TYPE: &'static str = "hashchange";
818 }
819
820 reference_boilerplate! {
821 HashChangeEvent,
822 instanceof HashChangeEvent
823 convertible to Event
824 }
825
826 impl HashChangeEvent {
827 /// The previous URL from which the window was navigated.
828 ///
829 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
830 #[inline]
old_url( &self ) -> String831 pub fn old_url( &self ) -> String {
832 js!(
833 return @{self.as_ref()}.oldURL;
834 ).try_into().unwrap()
835 }
836
837 /// The new URL to which the window was navigated.
838 ///
839 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
840 #[inline]
new_url( &self ) -> String841 pub fn new_url( &self ) -> String {
842 js!(
843 return @{self.as_ref()}.newURL;
844 ).try_into().unwrap()
845 }
846 }
847
848 /// The `IProgressEvent` interface represents progress-related
849 /// events.
850 ///
851 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent)
852 pub trait IProgressEvent: IEvent {
853 /// Indicates whether the progress is measureable.
854 ///
855 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/lengthComputable)
856 #[inline]
length_computable( &self ) -> bool857 fn length_computable( &self ) -> bool {
858 js!(
859 return @{self.as_ref()}.lengthComputable;
860 ).try_into().unwrap()
861 }
862
863 /// Returns the amount of work already performed by the underlying process.
864 ///
865 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/loaded)
866 #[inline]
loaded( &self ) -> u64867 fn loaded( &self ) -> u64 {
868 js!(
869 return @{self.as_ref()}.loaded;
870 ).try_into().unwrap()
871 }
872
873 /// Returns the total amount of work that the underlying process will perform.
874 ///
875 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/total)
876 #[inline]
total( &self ) -> u64877 fn total( &self ) -> u64 {
878 js!(
879 return @{self.as_ref()}.total;
880 ).try_into().unwrap()
881 }
882 }
883
884 /// A reference to a JavaScript object which implements the [IProgressEvent](trait.IProgressEvent.html)
885 /// interface.
886 ///
887 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent)
888 pub struct ProgressRelatedEvent( Reference );
889
890 impl IEvent for ProgressRelatedEvent {}
891 impl IProgressEvent for ProgressRelatedEvent {}
892
893 reference_boilerplate! {
894 ProgressRelatedEvent,
895 instanceof ProgressEvent
896 convertible to Event
897 }
898
899 /// The `ProgressEvent` is fired to indicate that an operation is in progress.
900 ///
901 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/progress)
902 pub struct ProgressEvent( Reference );
903
904 impl IEvent for ProgressEvent {}
905 impl IProgressEvent for ProgressEvent {}
906 impl ConcreteEvent for ProgressEvent {
907 const EVENT_TYPE: &'static str = "progress";
908 }
909
910 reference_boilerplate! {
911 ProgressEvent,
912 instanceof ProgressEvent
913 convertible to Event
914 convertible to ProgressRelatedEvent
915 }
916
917 /// The `LoadStartEvent` is fired when progress has begun on the loading of a resource.
918 ///
919 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/loadstart)
920 pub struct LoadStartEvent( Reference );
921
922 impl IEvent for LoadStartEvent {}
923 impl IProgressEvent for LoadStartEvent {}
924 impl ConcreteEvent for LoadStartEvent {
925 const EVENT_TYPE: &'static str = "loadstart";
926 }
927
928 reference_boilerplate! {
929 LoadStartEvent,
930 instanceof ProgressEvent
931 convertible to Event
932 convertible to ProgressRelatedEvent
933 }
934
935 /// The `LoadEndEvent` is fired when progress has stopped on the loading of a resource,
936 /// e.g. after `ErrorEvent`, `AbortEvent` or `LoadEvent` have been dispatched.
937 ///
938 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/loadend)
939 pub struct LoadEndEvent( Reference );
940
941 impl IEvent for LoadEndEvent {}
942 impl IProgressEvent for LoadEndEvent {}
943 impl ConcreteEvent for LoadEndEvent {
944 const EVENT_TYPE: &'static str = "loadend";
945 }
946
947 reference_boilerplate! {
948 LoadEndEvent,
949 instanceof ProgressEvent
950 convertible to Event
951 convertible to ProgressRelatedEvent
952 }
953
954 /// The `AbortEvent` is fired when the loading of a resource has been aborted.
955 ///
956 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/abort)
957 pub struct AbortEvent( Reference );
958
959 // TODO: This event is sometimes an UiEvent; what to do here?
960 impl IEvent for AbortEvent {}
961 impl ConcreteEvent for AbortEvent {
962 const EVENT_TYPE: &'static str = "abort";
963 }
964
965 reference_boilerplate! {
966 AbortEvent,
967 instanceof Event
968 convertible to Event
969 }
970
971 /// The `ErrorEvent` is fired when an error occurred; the exact circumstances vary,
972 /// since this event is used from a variety of APIs.
973 ///
974 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/error)
975 pub struct ErrorEvent( Reference );
976
977 // TODO: This event is sometimes an UiEvent; what to do here?
978 impl IEvent for ErrorEvent {}
979 impl ConcreteEvent for ErrorEvent {
980 const EVENT_TYPE: &'static str = "error";
981 }
982
983 reference_boilerplate! {
984 ErrorEvent,
985 instanceof Event
986 convertible to Event
987 }
988
989 impl ErrorEvent {
990 /// Returns a human-readable error message describing the problem.
991 ///
992 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/message)
993 #[inline]
message( &self ) -> String994 pub fn message( &self ) -> String {
995 return js!(
996 return @{self.as_ref()}.message;
997 ).try_into().unwrap()
998 }
999
1000 /// Returns the name of the script where the error occurred.
1001 ///
1002 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/filename)
1003 #[inline]
filename( &self ) -> String1004 pub fn filename( &self ) -> String {
1005 return js!(
1006 return @{self.as_ref()}.filename;
1007 ).try_into().unwrap()
1008 }
1009
1010 /// Returns the line number of the script file where the error occurred.
1011 ///
1012 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/lineNo)
1013 #[inline]
lineno( &self ) -> u321014 pub fn lineno( &self ) -> u32 {
1015 return js!(
1016 return @{self.as_ref()}.lineno;
1017 ).try_into().unwrap()
1018 }
1019
1020 /// Returns the column number of the script file where the error occurred.
1021 ///
1022 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/colNo)
1023 #[inline]
colno( &self ) -> u321024 pub fn colno( &self ) -> u32 {
1025 return js!(
1026 return @{self.as_ref()}.colno;
1027 ).try_into().unwrap()
1028 }
1029 }
1030
1031 #[cfg(web_api_tests)]
1032 mod tests {
1033 use super::*;
1034
1035 #[test]
test_event()1036 fn test_event() {
1037 let event: Event = js!(
1038 return new Event("dummy")
1039 ).try_into().unwrap();
1040
1041 assert_eq!( event.event_type(), "dummy" );
1042 assert_eq!( event.bubbles(), false );
1043 assert!( !event.cancel_bubble() );
1044 assert!( !event.cancelable(), false );
1045 assert!( event.current_target().is_none() );
1046 assert!( !event.default_prevented() );
1047 assert_eq!( event.event_phase(), EventPhase::None );
1048 assert!( event.target().is_none() );
1049 assert!( event.time_stamp().is_some() );
1050 assert!( !event.is_trusted() );
1051
1052 event.stop_immediate_propagation();
1053 event.stop_propagation();
1054 }
1055
1056 #[test]
test_change_event()1057 fn test_change_event() {
1058 let event: ChangeEvent = js!(
1059 return new Event( @{ChangeEvent::EVENT_TYPE} );
1060 ).try_into().unwrap();
1061 assert_eq!( event.event_type(), ChangeEvent::EVENT_TYPE );
1062 }
1063
1064 #[test]
test_ui_event()1065 fn test_ui_event() {
1066 let event: UiEvent = js!(
1067 return new UIEvent(
1068 @{ClickEvent::EVENT_TYPE},
1069 {
1070 detail: 1,
1071 }
1072 )
1073 ).try_into().unwrap();
1074 assert_eq!( event.event_type(), ClickEvent::EVENT_TYPE );
1075 assert_eq!( event.detail(), 1 );
1076 assert!( event.view().is_none() );
1077 }
1078
1079 #[test]
test_load_event()1080 fn test_load_event() {
1081 let event: UiEvent = js!(
1082 return new UIEvent( @{LoadEvent::EVENT_TYPE} );
1083 ).try_into().unwrap();
1084 assert_eq!( event.event_type(), LoadEvent::EVENT_TYPE );
1085 }
1086
1087 #[test]
test_mouse_event()1088 fn test_mouse_event() {
1089 let event: MouseEvent = js!(
1090 return new MouseEvent(
1091 @{ClickEvent::EVENT_TYPE},
1092 {
1093 altKey: false,
1094 button: 2,
1095 buttons: 6,
1096 clientX: 3.0,
1097 clientY: 4.0,
1098 ctrlKey: true,
1099 metaKey: false,
1100 screenX: 1.0,
1101 screenY: 2.0,
1102 shiftKey: true
1103 }
1104 );
1105 ).try_into().unwrap();
1106 assert_eq!( event.event_type(), ClickEvent::EVENT_TYPE );
1107 assert_eq!( event.alt_key(), false );
1108 assert_eq!( event.button(), MouseButton::Right );
1109 assert!( !event.buttons().is_down( MouseButton::Left ) );
1110 assert!( event.buttons().is_down( MouseButton::Right ) );
1111 assert!( event.buttons().is_down( MouseButton::Wheel ) );
1112 assert_eq!( event.client_x(), 3.0 );
1113 assert_eq!( event.client_y(), 4.0 );
1114 assert!( event.ctrl_key() );
1115 assert!( !event.get_modifier_state( ModifierKey::Alt ) );
1116 assert!( event.get_modifier_state( ModifierKey::Ctrl ) );
1117 assert!( event.get_modifier_state( ModifierKey::Shift ) );
1118 assert!( !event.meta_key() );
1119 assert_eq!( event.movement_x(), 0.0 );
1120 assert_eq!( event.movement_y(), 0.0 );
1121 assert!( event.region().is_none() );
1122 assert!( event.related_target().is_none() );
1123 assert_eq!( event.screen_x(), 1.0 );
1124 assert_eq!( event.screen_y(), 2.0 );
1125 assert!( event.shift_key() );
1126 }
1127
1128 #[test]
test_click_event()1129 fn test_click_event() {
1130 let event: ClickEvent = js!(
1131 return new MouseEvent( @{ClickEvent::EVENT_TYPE} );
1132 ).try_into().unwrap();
1133 assert_eq!( event.event_type(), ClickEvent::EVENT_TYPE );
1134 }
1135
1136 #[test]
test_double_click_event()1137 fn test_double_click_event() {
1138 let event: DoubleClickEvent = js!(
1139 return new MouseEvent( @{DoubleClickEvent::EVENT_TYPE} );
1140 ).try_into().unwrap();
1141 assert_eq!( event.event_type(), DoubleClickEvent::EVENT_TYPE );
1142 }
1143
1144 #[test]
test_keyboard_event()1145 fn test_keyboard_event() {
1146 let event: KeyboardEvent = js!(
1147 return new KeyboardEvent(
1148 @{KeypressEvent::EVENT_TYPE},
1149 {
1150 key: "A",
1151 code: "KeyA",
1152 location: 0,
1153 ctrlKey: true,
1154 shiftKey: false,
1155 altKey: true,
1156 metaKey: false,
1157 repeat: true,
1158 isComposing: false
1159 }
1160 );
1161 ).try_into().unwrap();
1162 assert!( event.alt_key() );
1163 assert_eq!( event.code(), "KeyA" );
1164 assert!( event.ctrl_key() );
1165 assert!( event.get_modifier_state( ModifierKey::Alt ) );
1166 assert!( event.get_modifier_state( ModifierKey::Ctrl ) );
1167 assert!( !event.get_modifier_state( ModifierKey::Shift ) );
1168 assert!( !event.is_composing() );
1169 assert_eq!( event.location(), KeyboardLocation::Standard );
1170 assert_eq!( event.key(), "A" );
1171 assert!( !event.meta_key() );
1172 assert!( event.repeat() );
1173 assert!( !event.shift_key() );
1174 }
1175
1176 #[test]
test_keypress_event()1177 fn test_keypress_event() {
1178 let event: KeypressEvent = js!(
1179 return new KeyboardEvent( @{KeypressEvent::EVENT_TYPE} );
1180 ).try_into().unwrap();
1181 assert_eq!( event.event_type(), KeypressEvent::EVENT_TYPE );
1182 }
1183
1184 #[test]
test_focus_event()1185 fn test_focus_event() {
1186 let event: FocusEvent = js!(
1187 return new FocusEvent( "focus" );
1188 ).try_into().unwrap();
1189 assert_eq!( event.event_type(), "focus" );
1190 assert!( event.related_target().is_none() );
1191 }
1192
1193 #[test]
test_blur_event()1194 fn test_blur_event() {
1195 let event: BlurEvent = js!(
1196 return new FocusEvent( @{BlurEvent::EVENT_TYPE} );
1197 ).try_into().unwrap();
1198 assert_eq!( event.event_type(), BlurEvent::EVENT_TYPE );
1199 }
1200
1201 #[test]
test_hash_change_event()1202 fn test_hash_change_event() {
1203 let event: HashChangeEvent = js!(
1204 return new HashChangeEvent(
1205 @{HashChangeEvent::EVENT_TYPE},
1206 {
1207 oldURL: "http://test.com#foo",
1208 newURL: "http://test.com#bar"
1209 }
1210 );
1211 ).try_into().unwrap();
1212 assert_eq!( event.event_type(), HashChangeEvent::EVENT_TYPE );
1213 assert_eq!( event.old_url(), "http://test.com#foo" );
1214 assert_eq!( event.new_url(), "http://test.com#bar" );
1215 }
1216
1217 #[test]
test_progress_event()1218 fn test_progress_event() {
1219 let event: ProgressEvent = js!(
1220 return new ProgressEvent(
1221 @{ProgressEvent::EVENT_TYPE},
1222 {
1223 lengthComputable: true,
1224 loaded: 10,
1225 total: 100,
1226 }
1227 );
1228 ).try_into().unwrap();
1229 assert_eq!( event.event_type(), ProgressEvent::EVENT_TYPE );
1230 assert!( event.length_computable() );
1231 assert_eq!( event.loaded(), 10 );
1232 assert_eq!( event.total(), 100 );
1233 }
1234
1235 #[test]
test_load_start_event()1236 fn test_load_start_event() {
1237 let event: LoadStartEvent = js!(
1238 return new ProgressEvent( @{LoadStartEvent::EVENT_TYPE} );
1239 ).try_into().unwrap();
1240 assert_eq!( event.event_type(), LoadStartEvent::EVENT_TYPE );
1241 }
1242
1243 #[test]
test_load_end_event()1244 fn test_load_end_event() {
1245 let event: LoadEndEvent = js!(
1246 return new ProgressEvent( @{LoadEndEvent::EVENT_TYPE} );
1247 ).try_into().unwrap();
1248 assert_eq!( event.event_type(), LoadEndEvent::EVENT_TYPE );
1249 }
1250
1251 #[test]
test_abort_event()1252 fn test_abort_event() {
1253 let event: AbortEvent = js!(
1254 return new Event( @{AbortEvent::EVENT_TYPE} );
1255 ).try_into().unwrap();
1256 assert_eq!( event.event_type(), AbortEvent::EVENT_TYPE );
1257 }
1258
1259 #[test]
test_error_event()1260 fn test_error_event() {
1261 let event: ErrorEvent = js!(
1262 return new ErrorEvent(
1263 @{ErrorEvent::EVENT_TYPE},
1264 {
1265 message: "Dummy error",
1266 filename: "Dummy.js",
1267 lineno: 5,
1268 colno: 10
1269 }
1270 );
1271 ).try_into().unwrap();
1272 assert_eq!( event.event_type(), ErrorEvent::EVENT_TYPE );
1273 assert_eq!( event.message(), "Dummy error".to_string() );
1274 assert_eq!( event.filename(), "Dummy.js".to_string() );
1275 assert_eq!( event.lineno(), 5 );
1276 assert_eq!( event.colno(), 10 );
1277 }
1278 }
1279