resources: Update OOjs-Router from 48302a572 to v0.1.0
[lhc/web/wiklou.git] / resources / lib / oojs-router / oojs-router.js
1 /*!
2 * OOjs Router v0.1.0
3 * https://www.mediawiki.org/wiki/OOjs_Router
4 *
5 * Copyright 2011-2019 OOjs Team and other contributors.
6 * Released under the MIT license
7 * http://oojs.mit-license.org
8 *
9 * Date: 2019-01-11T03:40:55Z
10 */
11 ( function ( $ ) {
12
13 'use strict';
14
15 /**
16 * Provides navigation routing and location information
17 *
18 * @class OO.Router
19 * @extends OO.Registry
20 */
21 OO.Router = function OoRouter() {
22 var router = this;
23
24 // Parent constructor
25 OO.Router.parent.call( this );
26
27 this.enabled = true;
28 this.oldHash = this.getPath();
29
30 $( window ).on( 'popstate', function () {
31 router.emit( 'popstate' );
32 } );
33
34 $( window ).on( 'hashchange', function () {
35 router.emit( 'hashchange' );
36 } );
37
38 this.on( 'hashchange', function () {
39 // event.originalEvent.newURL is undefined on Android 2.x
40 var routeEvent;
41
42 if ( router.enabled ) {
43 routeEvent = $.Event( 'route', {
44 path: router.getPath()
45 } );
46 router.emit( 'route', routeEvent );
47
48 if ( !routeEvent.isDefaultPrevented() ) {
49 router.checkRoute();
50 } else {
51 // if route was prevented, ignore the next hash change and revert the
52 // hash to its old value
53 router.enabled = false;
54 router.navigate( router.oldHash );
55 }
56 } else {
57 router.enabled = true;
58 }
59
60 router.oldHash = router.getPath();
61 } );
62 };
63
64 /* Inheritance */
65
66 OO.inheritClass( OO.Router, OO.Registry );
67
68 /* Events */
69
70 /**
71 * @event popstate
72 */
73
74 /**
75 * @event hashchange
76 */
77
78 /**
79 * @event route
80 * @param {jQuery.Event} routeEvent
81 */
82
83 /* Static Methods */
84
85 /**
86 * Determine if current browser supports this router
87 *
88 * @return {boolean} The browser is supported
89 */
90 OO.Router.static.isSupported = function () {
91 return 'onhashchange' in window;
92 };
93
94 /* Methods */
95
96 /**
97 * Check the current route and run appropriate callback if it matches.
98 */
99 OO.Router.prototype.checkRoute = function () {
100 var id, entry, match,
101 hash = this.getPath();
102
103 for ( id in this.registry ) {
104 entry = this.registry[ id ];
105 match = hash.match( entry.path );
106 if ( match ) {
107 entry.callback.apply( this, match.slice( 1 ) );
108 return;
109 }
110 }
111 };
112
113 /**
114 * Bind a specific callback to a hash-based route, e.g.
115 *
116 * @example
117 * addRoute( 'alert', function () { alert( 'something' ); } );
118 * addRoute( /hi-(.*)/, function ( name ) { alert( 'Hi ' + name ) } );
119 *
120 * Note that after defining all available routes it is up to the caller
121 * to check the existing route via the checkRoute method.
122 *
123 * @param {string|RegExp} path Path to match, string or regular expression
124 * @param {Function} callback Callback to be run when hash changes to one
125 * that matches.
126 */
127 OO.Router.prototype.addRoute = function ( path, callback ) {
128 var entry = {
129 path: typeof path === 'string' ?
130 new RegExp( '^' + path.replace( /[\\^$*+?.()|[\]{}]/g, '\\$&' ) + '$' ) :
131 path,
132 callback: callback
133 };
134 this.register( entry.path.toString(), entry );
135 };
136
137 /**
138 * @deprecated Use #addRoute
139 */
140 OO.Router.prototype.route = OO.Router.prototype.addRoute;
141
142 /**
143 * Navigate to a specific route.
144 *
145 * @param {string} path String with a route (hash without #).
146 */
147 OO.Router.prototype.navigate = function ( path ) {
148 var history = window.history;
149 // Take advantage of `pushState` when available, to clear the hash and
150 // not leave `#` in the history. An entry with `#` in the history has
151 // the side-effect of resetting the scroll position when navigating the
152 // history.
153 if ( path === '' && history && history.pushState ) {
154 // To clear the hash we need to cut the hash from the URL.
155 path = window.location.href.replace( /#.*$/, '' );
156 history.pushState( null, document.title, path );
157 this.checkRoute();
158 } else {
159 window.location.hash = path;
160 }
161 };
162
163 /**
164 * Navigate to the previous route. This is a wrapper for window.history.back
165 *
166 * @return {jQuery.Promise} Promise which resolves when the back navigation is complete
167 */
168 OO.Router.prototype.back = function () {
169 var timeoutID,
170 router = this,
171 deferred = $.Deferred();
172
173 this.once( 'popstate', function () {
174 clearTimeout( timeoutID );
175 deferred.resolve();
176 } );
177
178 window.history.back();
179
180 // If for some reason (old browser, bug in IE/windows 8.1, etc) popstate doesn't fire,
181 // resolve manually. Since we don't know for sure which browsers besides IE10/11 have
182 // this problem, it's better to fall back this way rather than singling out browsers
183 // and resolving the deferred request for them individually.
184 // See https://connect.microsoft.com/IE/feedback/details/793618/history-back-popstate-not-working-as-expected-in-webview-control
185 // Give browser a few ms to update its history.
186 timeoutID = setTimeout( function () {
187 router.off( 'popstate' );
188 deferred.resolve();
189 }, 50 );
190
191 return deferred.promise();
192 };
193
194 /**
195 * Get current path (hash).
196 *
197 * @return {string} Current path.
198 */
199 OO.Router.prototype.getPath = function () {
200 return window.location.hash.slice( 1 );
201 };
202
203 /**
204 * @deprecated Use static method
205 */
206 OO.Router.prototype.isSupported = OO.Router.static.isSupported;
207
208 if ( typeof module !== 'undefined' && module.exports ) {
209 module.exports = OO.Router;
210 }
211
212 }( jQuery ) );