1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | var debug = require('debug')('RouterMixin');
|
9 | var navigateAction = require('../actions/navigate');
|
10 | var History = require('./History');
|
11 | var React = require('react');
|
12 | var TYPE_CLICK = 'click';
|
13 | var TYPE_PAGELOAD = 'pageload';
|
14 | var TYPE_POPSTATE = 'popstate';
|
15 | var TYPE_DEFAULT = 'default';
|
16 | var RouterMixin;
|
17 |
|
18 | require('setimmediate');
|
19 |
|
20 | function routesEqual(route1, route2) {
|
21 | route1 = route1 || {};
|
22 | route2 = route2 || {};
|
23 | return (route1.url === route2.url);
|
24 | }
|
25 |
|
26 | function saveScrollPosition(e, history) {
|
27 | var historyState = (history.getState && history.getState()) || {};
|
28 | historyState.scroll = {x: window.scrollX, y: window.scrollY};
|
29 | debug('remember scroll position', historyState.scroll);
|
30 | history.replaceState(historyState);
|
31 | }
|
32 |
|
33 | RouterMixin = {
|
34 | contextTypes: {
|
35 | executeAction: React.PropTypes.func,
|
36 | makePath: React.PropTypes.func
|
37 | },
|
38 | componentDidMount: function() {
|
39 | var self = this;
|
40 | var context;
|
41 | var urlFromHistory;
|
42 | var urlFromState;
|
43 |
|
44 | if (self.context && self.context.executeAction) {
|
45 | context = self.context;
|
46 | } else if (self.props.context && self.props.context.executeAction) {
|
47 | context = self.props.context;
|
48 | }
|
49 |
|
50 | self._history = ('function' === typeof self.props.historyCreator) ? self.props.historyCreator() : new History();
|
51 | self._enableScroll = (self.props.enableScroll !== false);
|
52 |
|
53 | if (self.props.checkRouteOnPageLoad) {
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | urlFromHistory = self._history.getUrl();
|
62 | urlFromState = self.state && self.state.route && self.state.route.url;
|
63 |
|
64 | if (context && (urlFromHistory !== urlFromState)) {
|
65 |
|
66 |
|
67 | debug('pageload navigate to actual route', urlFromHistory, urlFromState);
|
68 | setImmediate(function navigateToActualRoute() {
|
69 | context.executeAction(navigateAction, {type: TYPE_PAGELOAD, url: urlFromHistory});
|
70 | });
|
71 | }
|
72 | }
|
73 |
|
74 | self._historyListener = function (e) {
|
75 | if (context) {
|
76 | var url = self._history.getUrl();
|
77 | debug('history listener invoked', e, url, self.state.route.url);
|
78 | if (url !== self.state.route.url) {
|
79 | context.executeAction(navigateAction, {type: TYPE_POPSTATE, url: url, params: (e.state && e.state.params)});
|
80 | }
|
81 | }
|
82 | };
|
83 | self._history.on(self._historyListener);
|
84 |
|
85 | if (self._enableScroll) {
|
86 | var scrollTimer;
|
87 | self._scrollListener = function (e) {
|
88 | if (scrollTimer) {
|
89 | window.clearTimeout(scrollTimer);
|
90 | }
|
91 | scrollTimer = window.setTimeout(saveScrollPosition.bind(self, e, self._history), 150);
|
92 | };
|
93 | window.addEventListener('scroll', self._scrollListener);
|
94 | }
|
95 | },
|
96 | componentWillUnmount: function() {
|
97 | this._history.off(this._historyListener);
|
98 | this._historyListener = null;
|
99 |
|
100 | if (this._enableScroll) {
|
101 | window.removeEventListener('scroll', this._scrollListener);
|
102 | this._scrollListener = null;
|
103 | }
|
104 |
|
105 | this._history = null;
|
106 | },
|
107 | componentDidUpdate: function (prevProps, prevState) {
|
108 | debug('component did update', prevState, this.state);
|
109 |
|
110 | var newState = this.state;
|
111 | if (routesEqual(prevState && prevState.route, newState && newState.route)) {
|
112 | return;
|
113 | }
|
114 |
|
115 | var nav = newState.route.navigate;
|
116 | var navType = (nav && nav.type) || TYPE_DEFAULT;
|
117 | var historyState;
|
118 |
|
119 | switch (navType) {
|
120 | case TYPE_CLICK:
|
121 | case TYPE_DEFAULT:
|
122 | historyState = {params: (nav && nav.params) || {}};
|
123 | if (this._enableScroll) {
|
124 | window.scrollTo(0, 0);
|
125 | historyState.scroll = {x: 0, y: 0};
|
126 | debug('on click navigation, reset scroll position to (0, 0)');
|
127 | }
|
128 | this._history.pushState(historyState, null, newState.route.url);
|
129 | break;
|
130 | case TYPE_POPSTATE:
|
131 | if (this._enableScroll) {
|
132 | historyState = (this._history.getState && this._history.getState()) || {};
|
133 | var scroll = (historyState && historyState.scroll) || {};
|
134 | debug('on popstate navigation, restore scroll position to ', scroll);
|
135 | window.scrollTo(scroll.x || 0, scroll.y || 0);
|
136 | }
|
137 | break;
|
138 | }
|
139 | }
|
140 | };
|
141 |
|
142 | module.exports = RouterMixin;
|