1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use webcore::value::{Reference, Value};
use webcore::try_from::TryInto;
use webapi::event::{IEvent, Event, ConcreteEvent};

/// The `HashChangeEvent` is fired when the fragment
/// identifier of the URL has changed (the part of the URL
/// that follows the # symbol, including the # symbol).
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/hashchange)
// https://html.spec.whatwg.org/#event-hashchange
// https://html.spec.whatwg.org/#hashchangeevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "HashChangeEvent")]
#[reference(subclass_of(Event))]
pub struct HashChangeEvent( Reference );

impl IEvent for HashChangeEvent {}
impl ConcreteEvent for HashChangeEvent {
    const EVENT_TYPE: &'static str = "hashchange";
}

impl HashChangeEvent {
    /// The previous URL from which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-oldurl
    #[inline]
    pub fn old_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.oldURL;
        ).try_into().unwrap()
    }

    /// The new URL to which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-newurl
    #[inline]
    pub fn new_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.newURL;
        ).try_into().unwrap()
    }
}

/// A popstate event is dispatched to the window every time the active history entry changes
/// between two history entries for the same document. If the history entry being activated was
/// created by a call to history.push_state() or was affected by a call to history.replace_state(),
/// the popstate event's state property contains a copy of the history entry's state object.
///
/// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent)
// https://html.spec.whatwg.org/#event-popstate
// https://html.spec.whatwg.org/#popstateevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Event")] // TODO: Better type check.
#[reference(subclass_of(Event))]
pub struct PopStateEvent(Reference);

impl PopStateEvent {
    /// The state object associated to the new history entry, if that entry was created with
    /// push_state or affected by replace_state.
    ///
    /// Example usage:
    ///
    /// ```rust,ignore
    /// let state: Option<MyStruct> = event.state().try_into().ok();
    /// ```
    // https://html.spec.whatwg.org/#dom-popstateevent-state
    #[inline]
    pub fn state(&self) -> Value {
        js!(return @{self}.state;)
    }
}

impl IEvent for PopStateEvent {}

impl ConcreteEvent for PopStateEvent {
    const EVENT_TYPE: &'static str = "popstate";
}

#[cfg(all(test, feature = "web_test"))]
mod tests {
    use super::*;

    #[test]
    fn test_hash_change_event() {
        let event: HashChangeEvent = js!(
            return new HashChangeEvent(
                @{HashChangeEvent::EVENT_TYPE},
                {
                    oldURL: "http://test.com#foo",
                    newURL: "http://test.com#bar"
                }
            );
        ).try_into().unwrap();
        assert_eq!( event.event_type(), HashChangeEvent::EVENT_TYPE );
        assert_eq!( event.old_url(), "http://test.com#foo" );
        assert_eq!( event.new_url(), "http://test.com#bar" );
    }

    #[test]
    fn test_pop_state_event() {
        let event: PopStateEvent = js!(
            return new PopStateEvent(
                @{PopStateEvent::EVENT_TYPE},
                {
                    state: {
                        color: "tomato"
                    }
                }
            );
        ).try_into().unwrap();

        assert_eq!(event.event_type(), PopStateEvent::EVENT_TYPE);

        let state_value: Value = event.state();
        let state: ::std::collections::BTreeMap<String, Value> = state_value
            .as_object()
            .unwrap()
            .into();
        let mut expected = ::std::collections::BTreeMap::new();
        expected.insert("color".to_string(), "tomato".into());

        assert_eq!(state, expected);
    }
}