Skip to content

Commit af7eb55

Browse files
agundermannmjackson
authored andcommitted
[added] History.onBeforeChange
1 parent 23c0aff commit af7eb55

File tree

3 files changed

+103
-12
lines changed

3 files changed

+103
-12
lines changed

modules/DOMHistory.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class DOMHistory extends History {
88
constructor(options={}) {
99
super(options);
1010
this.getScrollPosition = options.getScrollPosition || getWindowScrollPosition;
11+
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
1112
}
1213

1314
go(n) {
@@ -35,6 +36,34 @@ class DOMHistory extends History {
3536
return null;
3637
}
3738

39+
onBeforeChange(listener) {
40+
if (!this.beforeChangeListener && listener) {
41+
if (window.addEventListener) {
42+
window.addEventListener('beforeunload', this.handleBeforeUnload);
43+
} else {
44+
window.attachEvent('onbeforeunload', this.handleBeforeUnload);
45+
}
46+
} else if(this.beforeChangeListener && !listener) {
47+
if (window.removeEventListener) {
48+
window.removeEventListener('beforeunload', this.handleBeforeUnload);
49+
} else {
50+
window.detachEvent('onbeforeunload', this.handleBeforeUnload);
51+
}
52+
}
53+
54+
super.onBeforeChange(listener);
55+
}
56+
57+
handleBeforeUnload(event) {
58+
var message = this.beforeChangeListener.call(this);
59+
60+
if (message != null) {
61+
// cross browser, see https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload
62+
(event || window.event).returnValue = message;
63+
return message;
64+
}
65+
}
66+
3867
pushState(state, path) {
3968
var location = this.location;
4069
if (location && location.state && location.state.key) {

modules/History.js

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class History {
2828

2929
this.parseQueryString = options.parseQueryString || parseQueryString;
3030
this.changeListeners = [];
31+
32+
this.location = null;
33+
this._pendingLocation = null;
3134
}
3235

3336
_notifyChange() {
@@ -45,6 +48,10 @@ class History {
4548
});
4649
}
4750

51+
onBeforeChange(listener) {
52+
this.beforeChangeListener = listener;
53+
}
54+
4855
setup(path, entry = {}) {
4956
if (this.location)
5057
return;
@@ -56,15 +63,30 @@ class History {
5663
if (typeof this.readState === 'function')
5764
state = this.readState(entry.key);
5865

59-
this._update(path, state, entry, NavigationTypes.POP, false);
66+
var location = this._createLocation(path, state, entry, NavigationTypes.POP);
67+
this._update(path, location, false);
6068
}
6169

62-
handlePop(path, entry = {}) {
70+
handlePop(path, entry={}, applyEntry=null) {
6371
var state = null;
6472
if (entry.key && typeof this.readState === 'function')
6573
state = this.readState(entry.key);
6674

67-
this._update(path, state, entry, NavigationTypes.POP);
75+
var location = this._createLocation(path, state, entry, NavigationTypes.POP);
76+
77+
if (!this.beforeChangeListener) {
78+
applyEntry && applyEntry();
79+
this._update(path, location);
80+
} else {
81+
this._pendingLocation = location;
82+
this.beforeChangeListener.call(this, location, () => {
83+
if (this._pendingLocation === location){
84+
this._pendingLocation = null;
85+
applyEntry && applyEntry();
86+
this._update(path, location);
87+
}
88+
});
89+
}
6890
}
6991

7092
createRandomKey() {
@@ -88,9 +110,27 @@ class History {
88110
}
89111

90112
pushState(state, path) {
91-
var key = this._saveNewState(state);
113+
if (!this.beforeChangeListener) {
114+
this._doPushState(state, path);
115+
} else {
116+
var pendingLocation = this._createLocation(path, state, null, NavigationTypes.PUSH);
117+
this._pendingLocation = pendingLocation;
118+
119+
this.beforeChangeListener.call(this, pendingLocation, () => {
120+
if (this._pendingLocation === pendingLocation) {
121+
this._pendingLocation = null;
122+
this._doPushState(state, path);
123+
return true;
124+
}
125+
return false;
126+
});
127+
}
128+
}
92129

130+
_doPushState(state, path) {
131+
var key = this._saveNewState(state);
93132
var entry = null;
133+
94134
if (this.path === path) {
95135
entry = this.replace(path, key) || {};
96136
} else {
@@ -103,12 +143,30 @@ class History {
103143
this.constructor.name
104144
);
105145

106-
this._update(path, state, entry, NavigationTypes.PUSH);
146+
var location = this._createLocation(path, state, entry, NavigationTypes.PUSH);
147+
this._update(path, location);
107148
}
108149

109150
replaceState(state, path) {
110-
var key = this._saveNewState(state);
151+
if (!this.beforeChangeListener) {
152+
this._doReplaceState(state, path);
153+
} else {
154+
var pendingLocation = this._createLocation(path, state, null, NavigationTypes.REPLACE);
155+
this._pendingLocation = pendingLocation;
156+
157+
this.beforeChangeListener.call(this, pendingLocation, () => {
158+
if (this._pendingLocation === pendingLocation) {
159+
this._pendingLocation = null;
160+
this._doReplaceState(state, path);
161+
return true;
162+
}
163+
return false;
164+
});
165+
}
166+
}
111167

168+
_doReplaceState(state, path) {
169+
var key = this._saveNewState(state);
112170
var entry = this.replace(path, key) || {};
113171

114172
warning(
@@ -117,7 +175,8 @@ class History {
117175
this.constructor.name
118176
);
119177

120-
this._update(path, state, entry, NavigationTypes.REPLACE);
178+
var location = this._createLocation(path, state, entry, NavigationTypes.REPLACE);
179+
this._update(path, location);
121180
}
122181

123182
back() {
@@ -128,9 +187,10 @@ class History {
128187
this.go(1);
129188
}
130189

131-
_update(path, state, entry, navigationType, notify=true) {
190+
_update(path, location, notify=true) {
132191
this.path = path;
133-
this.location = this._createLocation(path, state, entry, navigationType);
192+
this.location = location;
193+
this._pendingLocation = null;
134194

135195
if (notify)
136196
this._notifyChange();

modules/MemoryHistory.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ class MemoryHistory extends History {
9797
this.constructor.name, n
9898
);
9999

100-
this.current += n;
101-
var currentEntry = this.entries[this.current];
100+
var next = this.current + n;
101+
var nextEntry = this.entries[next];
102102

103-
this.handlePop(currentEntry.path, { key: currentEntry.key, current: this.current });
103+
this.handlePop(nextEntry.path, { key: nextEntry.key, current: this.current }, () => {
104+
this.current = next;
105+
});
104106
}
105107

106108
canGo(n) {

0 commit comments

Comments
 (0)